What GMP Adds to Normal Coding Practice

IEC 61131-3 is a manufacturing standard — it defines syntax, semantics, and programming models for PLC languages, but says nothing about pharmaceutical compliance. GAMP 5 is a validation framework — it defines what evidence you need to demonstrate a system is fit for its intended use, but doesn't specify how you write a function block. GMP-compliant PLC code sits at the intersection: it must satisfy the programming discipline that IEC 61131-3 enables and the evidential requirements that GAMP 5 demands.

In practice, GMP adds four requirements on top of normal good coding practice:

Language Selection — The GMP Perspective

IEC 61131-3 defines five languages: Ladder Diagram (LD), Function Block Diagram (FBD), Structured Text (ST), Instruction List (IL — deprecated in the 3rd edition), and Sequential Function Chart (SFC). On most modern platforms — TIA Portal, Studio 5000, Codesys — you can mix languages within a project, using each for the type of logic it is best suited to. That is also the GMP-preferred approach, because it aligns each language with its reviewability advantage.

IEC 61131-3 LANGUAGE SELECTION FOR GMP LADDER (LD) Best for: Discrete I/O logic Interlocks Motor/valve control QA-familiar Visual logic flow PREFERRED FOR INTERLOCK REVIEW FBD Best for: Signal processing PID loops FB interconnections Data flow visible Hardware engineers CONTROL LOOP ARCHITECTURE STRUCTURED TEXT (ST) Best for: Complex calculations Audit trail logic String handling Git-diffable Version-control friendly PREFERRED FOR AUDIT TRAIL FBs SFC Best for: Batch sequences CIP/SIP phases Multi-step processes Maps to URS steps State visible in test PREFERRED FOR BATCH SEQUENCES IL (DEPRECATED) Assembly-like Low readability AVOID ON NEW PHARMA PROJECTS Removed from 3rd edition 2013
// USE EACH LANGUAGE FOR WHAT IT IS BEST AT. LD FOR INTERLOCKS, ST FOR AUDIT TRAIL AND CALCULATIONS, SFC FOR SEQUENCES. AVOID IL ENTIRELY ON NEW PHARMA PROJECTS — LOW READABILITY MEANS LOW REVIEWABILITY.

Ladder Diagram remains the most QA-familiar language — it maps visually to relay logic that electrical engineers and validation specialists recognise. For this reason, interlock logic is often best written in LD even on projects where the rest of the code uses ST, because the code review will include QA reviewers who are not ST-literate. A QA reviewer can follow a rung that says "pump runs AND level is above 20% AND no fault active, therefore start permissive is true." They may not be able to follow the same logic written as nested conditions in Structured Text.

Structured Text has the significant advantage for pharma of being text-based, which means it can be tracked in Git and diffed between versions — you can see exactly which lines of logic changed between v2.1 and v3.0. For audit trail function blocks, string handling, and complex calculation logic, ST is the right choice. It is also more concise than LD for complex conditional expressions, which reduces the visual noise that obscures logic during review.

Sequential Function Chart is the natural language for batch sequences and CIP/SIP phases because the states and transitions are visually explicit. An OQ tester can follow the sequence on screen as it executes and verify that the actual state transitions match the FDS description. For any process that has defined phases — fill, heat, hold, cool, drain — SFC makes both the design and the testing significantly cleaner than the equivalent LD implementation.

Function Block Headers — The Code Review Anchor

Every Function Block in a GMP PLC project must have a header comment that makes it self-documenting. The header is not optional and is not a nice-to-have — it is what the code reviewer reads first to understand what the block is supposed to do before verifying that it actually does it. Without a header, the reviewer must infer intent from the code itself, which is slower and more error-prone.

The minimum required header content for a pharma FB:

FUNCTION BLOCK HEADER — MINIMUM REQUIRED CONTENT (* ============================================================ FB_PID_GxP — GxP PID Control Loop Purpose : Implements a PID control loop for GxP-critical process variables. Includes audit trail logging on setpoint change, mode change, and output limit change. FDS Ref : FDS-SYS-001, Section 3.1 — PID Control Loops URS Ref : URS-FUN-001 through URS-FUN-005 Author : [Automation Engineer], [Company] Rev 1.0 : [Date] — Original issue Rev 1.1 : [Date] — CCR-007: Added manual output clamp Rev 2.0 : [Date] — CCR-012: Extended audit trail to output % ============================================================ *)

The FDS and URS references in the header are what allow the code reviewer to locate the design intent without searching. When the reviewer is checking that FB_PID_GxP correctly implements FDS Section 3.1, they need both the code and the FDS open — the header reference makes that navigation automatic. The revision history in the header must also match the version control tags: if CCR-007 is in the header, v1.1_CCR-007 must exist in the version archive.

Inline Comments — What Must Be Explained

Not every line of code needs a comment. Commenting b_PumpRun := TRUE; with "set pump run to true" adds noise without adding clarity. The standard for inline comments in a GMP context is: comment every non-obvious logic decision, every interlock condition, and every GxP-critical state transition.

INLINE COMMENT STANDARD — EXAMPLES // INTERLOCK: IL_004 — Pump start permissive requires level above 20% // Ref: CP-SYS-001 Section 4.2, URS-SAF-003 b_StartPermissive := (r_LevelPV > r_LevelMinStart) AND NOT b_FaultActive AND NOT b_ESDActive; // AUDIT TRAIL: Log setpoint change with old/new value and user ID // Required per FDS FUNC-DI-002 — any GxP parameter change must be logged IF r_SetpointNew <> r_SetpointPrev THEN FB_AuditTrail(s_Param := 'TIC101_SP', r_OldVal := r_SetpointPrev, r_NewVal := r_SetpointNew, s_User := s_ActiveUser, dt_Time := dt_SystemTime); r_SetpointPrev := r_SetpointNew; END_IF; // WARNING: Do NOT add Set/Reset latches for b_SterilComplete // State managed by FB_SterilizationSeq — single point of control // See SDS Section 2.2 Expert Principles and code review checklist item 4

The interlock comment referencing IL_004 and CP-SYS-001 is not decoration — it is what allows the code reviewer to cross-reference the implemented logic against the interlock register. If the comment says IL_004 but the interlock register in the SDS appendix doesn't contain IL_004, that is a gap found during review. The comment creates the traceability link that makes the finding possible.

GMP-Prohibited Coding Patterns

Several common PLC coding patterns that work fine in industrial automation are explicitly problematic in pharma. Understanding why helps avoid them systematically rather than just memorising a list of rules.

Set/Reset Latches on Critical Process States

An SR latch that holds a "batch complete" or "sterilisation done" state is untestable in isolation. To verify the state is correct, you must trace every Set instruction that could have asserted it and every Reset instruction that could have cleared it — across potentially multiple function blocks and OBs. More critically, a latch can retain a state across a CPU restart if the data block is retentive, which means a retained "batch complete" flag from a previous run could affect the next batch in ways that are difficult to detect. Use explicit state machines with defined entry and exit conditions instead.

Hardcoded Process Values

A temperature setpoint, alarm limit, or timer value hardcoded directly into a rung rather than referenced from a configurable data block cannot be changed through change control without a code change. On a validated system, that means a change control every time an operational parameter needs adjustment — even if the change is within the validated operating range. Configurable parameters must be in data blocks with appropriate access control. The URS acceptance criterion for that parameter should define the configurable range, and the OQ should verify that changes within the range are handled correctly and logged.

Multiple Write Points for the Same Physical Output

If a physical output — a valve command, a heater enable, a pump start — can be driven from multiple locations in the programme (from OB1, from OB35, from a safety interlock, and from a manual override), then there is no single authoritative source for that output state. Testing the output in an OQ test case verifies which write "won" at that moment, not that the system behaves consistently. Every physical output must have exactly one assignment instruction, in exactly one location, driven by virtual memory bits that aggregate all the conditions. All other logic writes to virtual bits. OB1 maps virtual bits to physical I/O at the end of each scan.

Commented-Out Production Code

Code that has been commented out and left in the programme creates confusion during code review — the reviewer cannot tell whether the commented section represents an intentional design decision, an abandoned approach, or a mistake. On a validated system, commented-out code that was once active is a change that should have gone through change control. Remove it, commit the removal as a tagged version referencing the relevant CCR, and the question is closed. Left in, it is a perpetual source of review questions.

The Code Review Will Find These

These four patterns — SR latches on critical states, hardcoded process values, multiple write points, commented-out code — appear on virtually every code review checklist for pharma PLC projects. They are not obscure requirements. If your code contains any of them, expect code review findings. Fixing them before the code review saves time; fixing them during the review creates additional documentation overhead and may require re-verification of the affected test cases.

Writing for Testability — Acceptance Criteria to Code

The risk assessment determines which functions need to be tested and at what depth. High-risk functions need to be tested against specific, measurable acceptance criteria. The code structure determines whether those test cases can actually be executed cleanly.

A test case for a temperature interlock might say: "Apply a simulated temperature value of 91°C to TT_R01_101. Verify that alarm HH_TT_101 activates within 1 second, the heater output HT_R01_001 de-energises immediately, and the audit trail records the alarm event with timestamp and process value." For that test to be executable, the code must:

If any of those structural elements are missing, the test case cannot be executed as written. The choice is to rewrite the test case (weakening the validation evidence) or fix the code (which may require re-review if caught late). Structuring the project correctly from the start is what prevents that choice from arising.

In the QLean Framework

The SDS template includes a Coding Standards appendix that documents all the standards described in this article as a controlled, project-specific reference. The code review checklist references each standard by appendix item, so the review is structured around the standards rather than relying on the reviewer's memory. The FAT and OQ protocol templates pre-include test cases for the GxP-critical code patterns — simulation bit injection, audit trail verification, setpoint change logging — so the testability requirements are built into the protocol structure from the start.