Independent and not affiliated with the FDA, MHRA, ISPE, PDA, or any agency. Get the appgoutham@madhadi.com
madhadi.comData Integrity & GxP Quality
Browse all topics → Articles Templates & Procedures Learning paths GlossaryScenariosToolsRegulatory ReferencesLearning PathsTopics About Start here
Specification Plug-and-play starting point CSV / CSA

Specification: Design Specification (DS) for Custom GxP Software (GAMP Category 5)

A plug-and-play Design Specification for GAMP Category 5 custom software: system architecture, data model, an independently verifiable step-by-step algorithm specification, interface specification, security design, error handling for every exception path, and configuration parameters, with a filled worked specimen.

Document type: Specification

Read and copy the template below into your own quality system. It is a generic starting point for your own internal use, provided as is, with no warranty; see the Terms and License. Adopting it does not by itself create compliance.

This is a ready-to-use Design Specification (DS) for a GAMP Category 5 custom system: a system where code was written or substantially modified for your own use, so there is no vendor design and no vendor testing to draw on. Replace every <<FILL: ...>> placeholder with your own specifics, set your document numbers and dates, and route it through your normal document control, review, and approval before coding starts. Writing this document before the code exists, not after, is what makes the code review and OQ meaningful rather than reverse-engineered. A worked filled specimen follows. This content is educational reference, not legal or regulatory advice; verify each cited regulation against the current source before you rely on it.

For the full validation lifecycle this document sits inside, see validating custom software: GAMP Category 5 from specification to release. Where the custom function is a trained model rather than a deterministic algorithm, section 5 below does not apply as written; see that article’s section on AI/ML-based custom functions for what replaces it (training data provenance, model versioning, performance qualification against a held-out set, and drift monitoring).

Document control header

FieldEntry
Document titleDesign Specification, <<FILL: system / component name>>
Document number<<FILL: DS-ID, e.g. DS-CUST-014>>
Version<<FILL: version, e.g. 1.0>>
Effective date<<FILL: effective date>>
Supersedes<<FILL: prior version or "New">>
Document owner<<FILL: role, e.g. System Owner / Technical Lead>>
GAMP category5 (custom code); component-level category if this DS covers a custom component inside a Category 4 platform: <<FILL>>
Linked documents<<FILL: URS-ID, FS-ID, RTM-ID, coding standard-ID, configuration management plan-ID>>
StatusApproved before coding starts: <<FILL: yes/no and date>>

1. Purpose

This specification records how <<FILL: system / component name>> is designed, so that the functions defined in functional specification <<FILL: FS-ID>> can be built without the developer having to guess, the code review can confirm the code matches the design, and the OQ can verify the design against real inputs, including inputs derived independently of the code itself. A Design Specification is the deliverable that exists in Category 5 and does not exist in Category 3 or 4, because in those categories the vendor owns the design. Here, you do.

2. Scope

This DS covers <<FILL: the specific system, component, or module>>. It includes the architecture in section 4, the data model in section 5, the algorithm specification in section 6, the interface specification in section 7, the security design in section 8, error handling in section 9, and configuration parameters in section 10. It does not cover <<FILL: out-of-scope items, e.g. the underlying database engine's own qualification, or a Category 4 platform this component runs inside>>, which is governed by <<FILL: cross-reference>>.

3. Responsibilities

RoleResponsibility
System owner / business ownerOwns the URS this DS ultimately traces to; confirms the design serves the actual business and GxP need
Technical lead / architectAuthors this DS; owns the architecture and keeps it current under change control
Developer(s)Builds to this DS and the coding standard; raises a design query rather than silently deviating when the DS is unclear or wrong
Independent code reviewerVerifies, line by line, that the delivered code implements this DS, including every error path and every configuration parameter
Validation lead / CSV specialistConfirms traceability from this DS to the FS above it and the unit test and OQ evidence below it
Quality AssuranceApproves this DS before coding starts and approves any subsequent change

4. System architecture

Describe the system’s components and how they interact, in enough detail that a reader understands why the system is structured the way it is and where the boundaries sit between components. Attach a component or block diagram; do not rely on prose alone to carry the structure.

ItemEntry
Component list<<FILL: name each module/component and its one-sentence purpose>>
Data flow between components<<FILL: which component calls or passes data to which, and in what direction>>
External dependencies<<FILL: libraries, services, instruments, or other systems this system depends on>>
Deployment topology<<FILL: where each component runs, e.g. single script on an analyst workstation, or a web application on an application server with a separate database>>
Component diagram reference<<FILL: Attachment 1>>

5. Data model

Specify the structure of every data entity the system creates, reads, updates, or deletes. For a database-backed system this is the table schema; for a flat-file system this is the file format. Identify which fields carry GxP-critical data, because those are the fields the error handling in section 9 and the access control in section 8 must protect.

Entity / table / fileFieldData typeConstraintGxP-critical?Notes
<<FILL: e.g. batch_yield_record>><<FILL: e.g. batch_id>><<FILL: e.g. varchar(20)>><<FILL: e.g. not null, foreign key>>Yes/No<<FILL>>
<<FILL>><<FILL: e.g. final_yield_pct>><<FILL: e.g. decimal(5,2)>><<FILL: e.g. 0.00 to 150.00>>Yes<<FILL: the controlled result field>>
<<FILL>><<FILL: e.g. calculated_by>><<FILL>><<FILL: not null, from authenticated session>>Yes<<FILL>>
<<FILL>><<FILL: e.g. calculation_timestamp>><<FILL: UTC datetime>><<FILL: system-generated, not editable>>Yes<<FILL>>

State explicitly which fields are system-generated and not user-editable, since a field that should be system-controlled but is left editable is a recurring finding.

6. Algorithm specification

This is the section that distinguishes a Category 5 DS from every configuration document in Category 3 or 4. For any calculation the system performs, the algorithm must be specified mathematically, step by step, before code is written, in enough detail that an inspector or independent reviewer can work through it on paper with the same inputs and arrive at the same output. “See the code” or a prose summary of what a function does is not an algorithm specification. If this function turns out to be implemented as a trained model rather than a fixed formula, stop here; this section format does not apply, and the model-specific deliverables described in the parent article’s AI/ML section apply instead.

6.1 Algorithm identity and inputs

FieldEntry
Algorithm ID<<FILL: ALG-001>>
Traces to FS requirement<<FILL: FS-ID>>
Plain-language purpose<<FILL: one sentence, what this calculation determines and what decision it feeds>>
Inputs (name, unit, valid range)<<FILL: list every input variable with its unit and the range of values it can validly take>>
Output (name, unit, precision)<<FILL: the controlled result field, its unit, and the number of decimal places or significant figures reported>>

6.2 Step-by-step specification

<<FILL: state the formula and every intermediate step explicitly, in the numbered structure below. Do not skip a step because it seems obvious; the point of this section is that nothing is left for the code to decide silently.>>

  1. <<FILL: Step 1, e.g. "Confirm the load titer and load volume are both present and greater than zero.">>
  2. <<FILL: Step 2, the first calculation, stated as an equation, e.g. "Calculate load mass (mg) = load titer (mg/mL) x load volume (mL).">>
  3. <<FILL: Step 3, e.g. "Calculate pool mass (mg) = pool titer (mg/mL) x pool volume (mL), using the same method.">>
  4. <<FILL: Step 4, the final result, e.g. "Calculate step yield (%) = (pool mass / load mass) x 100.">>
  5. <<FILL: Step 5, rounding rule, e.g. "Round the reported yield to one decimal place, round-half-up; retain the full-precision value in the log.">>
  6. <<FILL: Step 6, any conditional or derived flag, e.g. "If step yield is below <<FILL: threshold>>%, set result_flag = LOW_YIELD; if above <<FILL: threshold>>%, set result_flag = HIGH_YIELD; otherwise NORMAL.">>

6.3 Undefined and boundary conditions

State explicitly what the algorithm does when a mathematically undefined condition would otherwise occur (division by zero, a negative value where only positive is physically meaningful, a value at exactly a rounding boundary). An algorithm specification that is silent on these conditions leaves the code to invent behavior that nobody reviewed.

ConditionRequired behavior
<<FILL: e.g. load mass = 0>><<FILL: e.g. raise ERR-ALG-001, "Load mass cannot be zero", no result calculated>>
<<FILL: e.g. any input negative>><<FILL: e.g. raise ERR-ALG-002, reject before calculation begins>>
<<FILL: e.g. result exactly at the rounding half>><<FILL: e.g. round-half-up per step 5, not the language's default rounding behavior>>
<<FILL: e.g. result outside the physically plausible range, such as yield > 150%>><<FILL: e.g. calculate but set result_flag = IMPLAUSIBLE and require reviewer acknowledgement before the result is used>>

6.4 Independent verification worked example

Provide at least one complete worked example with real numbers, calculated by hand or an independent tool, that a reviewer can reproduce without running the code. This worked example becomes an OQ known-answer test case and a unit test case; do not derive it from the code’s own output.

FieldEntry
Example inputs<<FILL: e.g. load titer = 12.5 mg/mL, load volume = 400 mL, pool titer = 8.2 mg/mL, pool volume = 550 mL>>
Step-by-step hand calculation<<FILL: show the arithmetic at each step from section 6.2>>
Expected output<<FILL: e.g. load mass = 5000.0 mg; pool mass = 4510.0 mg; yield = 90.2%>>
Calculated by<<FILL: name, method, e.g. "hand calculation cross-checked in a spreadsheet">>
Date, before code exists or before this version’s code review<<FILL>>

7. Interface specification

Specify every external interface: data from instruments, data to other systems, user data-entry points, and API calls. For each, state the protocol, data format, authentication, and what happens when the interface fails.

Interface IDDirectionCounterpartyProtocol / formatAuthenticationBehavior on failure
<<FILL: INT-001>>Inbound<<FILL: e.g. plate reader export folder>><<FILL: e.g. CSV, UTF-8, defined column headers>><<FILL: e.g. file-share permission, service account>><<FILL: e.g. retry 3 times over 5 minutes, then raise ERR-INT-001 and notify the system owner>>
<<FILL: INT-002>>Outbound<<FILL: e.g. LIMS>><<FILL: e.g. REST API, JSON>><<FILL: e.g. API key, rotated per policy>><<FILL: e.g. queue locally, retry, alert if unresolved after <<FILL>> minutes>>
<<FILL: INT-003>>User interface<<FILL: e.g. operator entry screen>><<FILL: e.g. web form>><<FILL: e.g. session token, role-checked per field>><<FILL: e.g. reject submission, display the specific validation error>>

8. Security design

Record the roles, permissions, authentication method, and session controls, since this governs who can perform GxP functions and whose actions appear in the audit trail.

ItemEntry
Roles and what each can do<<FILL: e.g. Operator: enter and view; Reviewer: approve; Administrator: configure, cannot enter or approve GxP data>>
Authentication method<<FILL: e.g. SSO against corporate directory, or local account with defined password policy>>
Session timeout<<FILL: e.g. 15 minutes idle>>
Failed-login lockout<<FILL: e.g. 5 attempts, 30-minute lockout, administrator reactivation with reason>>
Separation of duties enforced<<FILL: e.g. the account that enters a result cannot approve the same result>>
Audit trail: events captured<<FILL: list every create, modify, delete, and configuration-change event, with the fields captured for each>>

9. Error handling

For every exception path the system can encounter, state the required behavior. An algorithm or interface specification that names an error code without stating what the system does with it (log, halt, notify, degrade) is incomplete. Cover every path identified in sections 6.3 and 7, plus the general paths below.

Error conditionDetection pointSystem responseLogged eventWhat the user sees
<<FILL: e.g. invalid input format>><<FILL: input validation, before calculation>><<FILL: reject, no partial result written>><<FILL: ERR code, timestamp, offending field>><<FILL: specific message naming the field>>
<<FILL: e.g. database write failure>><<FILL: on write attempt>><<FILL: retry per policy, then buffer or halt, per design decision>><<FILL>><<FILL>>
<<FILL: e.g. external interface unavailable>><<FILL: on call timeout>><<FILL: per interface table in section 7>><<FILL>><<FILL>>
<<FILL: e.g. calculation produces an undefined result>><<FILL: per section 6.3>><<FILL>><<FILL>><<FILL>>
<<FILL: e.g. unauthorized access attempt>><<FILL: on permission check>><<FILL: deny, no partial execution>><<FILL>><<FILL>>

A function returning a result as if it had succeeded, when it has actually failed silently, is the most dangerous class of error in GxP software. Every row above must resolve to either a visible failure or an explicit, reviewed exception; there is no third option.

10. Configuration parameters

List every value that controls system behavior but is not hardcoded. A hardcoded value that belongs here is a design deficiency, because every future change to it then requires a code change and a full code review rather than a controlled configuration change.

ParameterPurposeAllowable rangeDefaultStored whereChange requires
<<FILL: e.g. low_yield_threshold>><<FILL: flags results for review below this value>><<FILL: e.g. 0 to 100%>><<FILL: e.g. 80%>><<FILL: e.g. config table, admin-only write>><<FILL: change control, no code change>>
<<FILL: e.g. interface_retry_count>><<FILL>><<FILL>><<FILL>><<FILL>><<FILL>>
<<FILL: e.g. session_timeout_minutes>><<FILL>><<FILL>><<FILL>><<FILL>><<FILL>>

11. Traceability

Every algorithm, interface, security control, error path, and configuration parameter in this DS traces upward to a functional specification requirement and downward to the code review, unit test, and OQ evidence that verifies it. The RTM <<FILL: matrix ID>> is the authoritative cross-reference. No item in this DS is left without a verification reference before release.

12. Acceptance criteria for this specification

  • This DS was written and approved before coding started (dated evidence).
  • Every calculation the system performs has a step-by-step algorithm specification, an explicit treatment of undefined and boundary conditions, and at least one independently derived worked example.
  • Every external interface, security control, and configuration parameter is specified with enough detail to build and test against without further interpretation.
  • Every identified exception path resolves to a stated system response, a logged event, and a user-visible outcome; none is left as “handled by the code as appropriate.”
  • The document is traceable to the FS above it and to the code review, unit test, and OQ evidence below it, and is under change control.

13. References

21 CFR 211.68 (automatic, mechanical, and electronic equipment). 21 CFR Part 11 (electronic records and signatures). EU GMP Annex 11 (Computerised Systems). ISPE GAMP 5 (Second Edition), A Risk-Based Approach to Compliant GxP Computerized Systems, for the Category 5 lifecycle (reference by title; describe, do not paste). FDA guidance, Computer Software Assurance for Production and Quality Management System Software (current version). ICH Q9(R1), Quality Risk Management.

Confirm the current version and clause numbers of each reference before issue.

14. Revision history

VersionDateAuthorSummary of change
<<FILL: 1.0>><<FILL: date>><<FILL: author>>Initial issue, approved before coding started.

15. Approvals

RoleNameSignatureDate
Author (Technical Lead)<<FILL>>
System Owner<<FILL>>
Quality Assurance<<FILL>>

Filled specimen

The following shows sections 5, 6, and 9 completed for an illustrative custom script that calculates the step yield of a chromatography purification step from load and pool titer and volume, and flags the result for review if it falls outside a set range. The company, system, and numbers are illustrative; replace them with your own.

Data model (extract)

EntityFieldTypeConstraintGxP-critical
yield_recordbatch_idvarchar(20)not null, foreign key to batch recordYes
yield_recordload_titer_mg_mldecimal(6,2)> 0Yes
yield_recordload_volume_mldecimal(8,2)> 0Yes
yield_recordpool_titer_mg_mldecimal(6,2)> 0Yes
yield_recordpool_volume_mldecimal(8,2)> 0Yes
yield_recordstep_yield_pctdecimal(5,1)system-calculated, not editableYes, controlled result field
yield_recordresult_flagvarchar(12)NORMAL / LOW_YIELD / HIGH_YIELD / IMPLAUSIBLEYes

Algorithm specification: ALG-001, step yield calculation

Step-by-step:

  1. Confirm load_titer_mg_ml, load_volume_ml, pool_titer_mg_ml, and pool_volume_ml are all present and greater than zero; if not, raise ERR-ALG-001 and calculate nothing.
  2. Calculate load_mass_mg = load_titer_mg_ml x load_volume_ml.
  3. Calculate pool_mass_mg = pool_titer_mg_ml x pool_volume_ml.
  4. Calculate step_yield_pct_full = (pool_mass_mg / load_mass_mg) x 100.
  5. Round step_yield_pct to one decimal place, round-half-up; retain step_yield_pct_full in the log.
  6. If step_yield_pct < 70.0, set result_flag = LOW_YIELD. If step_yield_pct > 110.0, set result_flag = HIGH_YIELD. If step_yield_pct > 150.0, set result_flag = IMPLAUSIBLE and require reviewer acknowledgement before use. Otherwise, result_flag = NORMAL.

Undefined and boundary conditions: load_mass_mg = 0 raises ERR-ALG-001 before division is attempted, since the calculation would otherwise divide by zero; a step_yield_pct of exactly 70.0 or 110.0 is NORMAL, since the flags trigger strictly below or strictly above the threshold, not at it.

Independent verification worked example: load titer 12.5 mg/mL, load volume 400 mL, pool titer 8.2 mg/mL, pool volume 550 mL. Hand calculation: load_mass_mg = 12.5 x 400 = 5000.0 mg; pool_mass_mg = 8.2 x 550 = 4510.0 mg; step_yield_pct_full = (4510.0 / 5000.0) x 100 = 90.2%; result_flag = NORMAL (between 70.0 and 110.0). Calculated by hand and cross-checked in a spreadsheet on 15 August 2026, before this version’s code review.

Error handling (extract)

Error conditionDetection pointSystem responseLogged eventUser sees
load_mass_mg = 0Before division, per algorithm step 1Reject, no result calculatedERR-ALG-001, timestamp, batch ID”Load mass is zero; check load titer and volume entries”
step_yield_pct > 150.0After calculationResult written but flagged IMPLAUSIBLE, held from batch record until reviewer acknowledgesFlag event with the calculated value”Yield result exceeds the plausible range; reviewer acknowledgement required before use”

In this example, the algorithm is specified as an equation a reviewer can work through by hand, the boundary behavior at exactly 70.0 and 110.0 is stated explicitly rather than left to the code’s comparison operators, and the worked example is dated before the code review so it can serve as the unit test’s expected value rather than a value read off the running script.

Common inspection findings this specification prevents

  • No design specification exists for the custom software; the OQ test cases cannot be traced to a documented algorithm, so they are effectively testing the code against itself.
  • The algorithm is described in prose (“the script calculates yield”) with no equation, so no reviewer can independently confirm a given output is correct.
  • Boundary behavior (what happens exactly at a threshold) is left to the programming language’s default comparison and rounding behavior, undocumented and untested.
  • A configuration value that should be adjustable without a code change (a threshold, a limit) is hardcoded, so every future adjustment requires a full code change cycle.
  • An exception path exists in the code with no corresponding entry in the DS, discovered only during code review or, worse, in production.

How to adapt this specification

  1. Set your document number, owner, and GAMP category in the header, and link the URS, FS, and RTM this DS sits between.
  2. Replace section 4’s architecture with your actual components and attach a real diagram; do not describe a system with prose alone.
  3. Build section 5’s data model from your actual schema or file format, and mark every GxP-critical field.
  4. For every calculation your system performs, complete a full section 6 block: the step-by-step formula, the undefined/boundary conditions, and at least one hand-calculated worked example dated before the code review.
  5. Complete sections 7 through 10 for your real interfaces, security model, exception paths, and configuration parameters; leave nothing marked “handled as appropriate.”
  6. Confirm every regulation in section 13 against the current published version before issue.
Use madhadi.com as an app Full screen, works offline, one tap from your home screen.