Example: Batch lifecycle¶
The batch lifecycle document traces a single run of a console application from
the entry point through validation, initialisation, the main processing loop,
and finalisation. Doc gen produces one per application at
docs/applications/<app-slug>/batch-lifecycle.md.
What reviewers should check¶
- The
## Execution flowMermaid diagram uses a standard```mermaidfence; a legacy Azure DevOps colon-prefixed mermaid fence is a blocker. - The diagram matches the real entry point: the node names should correspond to methods that exist in the source, and both the pass and fail branches of validation must be shown.
ValidateCommandLine,PreProcess, andRunAppLogiceach have substantive prose, not a restated heading.- The processing pattern table states what happens on success and on failure for every step, including commit and rollback behaviour.
## Return codeslists every code the program can exit with, and each maps to a specific trigger an operator can recognise.- Error handling separates per-record failures from catastrophic failures, and the threshold that triggers notification is a concrete number.
Example¶
The document below is a complete, correct console-batch-lifecycle.md for a
fictional application.
View rendered example — the same document as a real page, so you can compare rendering as well as source.
---
title: Batch lifecycle
summary: Execution flow from entry point through processing to finalization.
status: draft
owner: Policy Systems Team
last_reviewed: 2026-02-18
---
# Batch lifecycle
A run of `SAIF.PremiumAudit.Batch` begins in `Sub Main` in `Program.vb`, which
constructs the `AuditProcessor` batch class and calls `Execute`. `Execute`
validates the command line, initialises the data access layer and log streams,
runs the per-audit processing loop, and finalises the run by writing totals and
closing the streams. Every exit path returns a numeric return code that the
Windows Task Scheduler task records as the last result.
## Execution flow
```mermaid
flowchart TD
MAIN[Sub Main] --> NEW[New AuditProcessor]
NEW --> EXEC[Execute]
EXEC --> VALIDATE[ValidateCommandLine]
VALIDATE -->|Pass| PREPROCESS[PreProcess]
VALIDATE -->|Fail| HELP[WriteHelp + Exit 1]
PREPROCESS -->|Pass| RUN[RunAppLogic]
PREPROCESS -->|Fail| EXIT1[Exit with ReturnCode 2]
RUN --> FINALIZE[Finalization]
FINALIZE --> EXIT2[Exit with ReturnCode]
```
## ValidateCommandLine
`ValidateCommandLine` checks the four positional arguments described in the
overview. It requires `Environment` to be one of `DEV`, `TEST`, or `PROD` and
requires `AuditDate` to parse as `yyyy-MM-dd` and to be no later than the
current date. `BatchSize`, when supplied, must be an integer between 1 and
5000; `RestartCheckpoint`, when supplied, must be a positive integer. Any
failure writes the usage text through `WriteHelp`, records the reason in the
File0 stream, and returns `ReturnCode.BadParameter` (1) without opening a
database connection.
## PreProcess
`PreProcess` resolves the connection string for the requested environment from
`App.config`, opens the SQL Server connection used by the data access layer, and
verifies it by executing a lightweight probe query. It then creates the three
output streams in the configured log directory, writes the File0 header block
(program name, assembly version, machine name, environment, audit date, batch
size, and restart checkpoint), and loads the `PayrollClassCode` rate table into
memory so that rate lookups during the loop do not require a round trip. A
failure to open the connection, create a stream, or load the rate table is
logged to File0 and returns `ReturnCode.InitializationFailure` (2).
## RunAppLogic
`RunAppLogic` calls `dbo.usp_GetPendingAudits`, passing the audit date, batch
size, and restart checkpoint, and receives the eligible `PolicyAudit` rows
ordered by `PolicyAuditId`. It iterates the rows one at a time. For each audit
it validates the payroll detail with `SAIF.Framework.Validation`, recalculates
earned premium from the audited payroll and the class code rate in effect on the
policy effective date, compares the result to the estimated premium, and writes
the difference as a `PremiumTransaction` row. Each audit is processed inside its
own database transaction so that a failure affects only that audit. After every
100 audits the current `PolicyAuditId` is written to File0 as a checkpoint line
so that a partial run can be resumed.
### Processing pattern
| Step | Action | On success | On failure |
| --- | --- | --- | --- |
| 1 | Retrieve pending audits via `dbo.usp_GetPendingAudits` | Continue to the processing loop | Log to File0 and exit with ReturnCode 3 |
| 2 | Validate payroll detail for the audit (BR-001, BR-002) | Continue to recalculation | Mark the audit `Rejected`, roll back, log to File2 |
| 3 | Recalculate premium and insert the `PremiumTransaction` row | Commit, update `PolicyAudit.Status` to `Posted`, log to File1 | Roll back the audit transaction, log to File2, continue with the next audit |
| 4 | Write run totals (audits read, posted, rejected, failed, net premium adjustment) to File0 | Streams closed and flushed | Log the write failure to File0 and exit with ReturnCode 4 |
| 5 | Compare the failure count to the configured threshold | Exit with ReturnCode 0 | Send the on-call email and exit with ReturnCode 5 |
## Batch streams
| Stream | Purpose | Audience |
| --- | --- | --- |
| `File0` | Control/execution log: header block, checkpoint lines every 100 audits, run totals, and fatal errors | Operations |
| `File1` | Success detail report: one line per posted audit with policy number, audited payroll, recalculated premium, and adjustment amount | Operations / Business |
| `File2` | Error detail report: one line per rejected or failed audit with policy number, `PolicyAuditId`, rule id, and exception message | Operations / On-call |
## Return codes
| Code | Meaning | Trigger |
| --- | --- | --- |
| 0 (Success) | Processing completed | All audits processed and the failure count is at or below the threshold |
| 1 (BadParameter) | Invalid parameters | ValidateCommandLine failure; usage text written to the console |
| 2 (InitializationFailure) | Initialization failed | Connection could not be opened, a log stream could not be created, or the rate table could not be loaded |
| 3 (RetrievalFailure) | Audit retrieval failed | `dbo.usp_GetPendingAudits` raised an error or timed out before any audit was processed |
| 4 (FinalizationFailure) | Totals could not be written | The File0 stream could not be flushed or closed at the end of the run |
| 5 (ErrorThresholdExceeded) | Too many per-audit failures | The failure count exceeded `ErrorThreshold` (default 25); notification email sent |
## Error handling
### Per-record errors
Each audit is processed inside its own SQL Server transaction opened at the
start of step 2 and committed at the end of step 3. Any validation failure or
exception raised while processing an audit causes the transaction to roll back,
so no partial `PremiumTransaction` row and no `PolicyAudit.Status` change
survive a failed audit. The failure is written to File2 with the policy number,
`PolicyAuditId`, the business rule id where applicable, and the exception
message, the failure counter is incremented, and the loop continues with the
next audit. Audits that fail this way keep their `Pending` status and are picked
up again on the next run.
### Catastrophic errors
Two conditions end a run early. A failure in `PreProcess` or in the retrieval
call means no audit can be processed, so the run exits immediately with return
code 2 or 3. A run that starts successfully but accumulates more than
`ErrorThreshold` per-audit failures (default 25, set in `App.config`) stops the
loop, writes the totals it has, sends the notification email to the on-call
distribution list configured under `NotificationRecipients`, and exits with
return code 5. In both cases the File0 stream records the last checkpoint
`PolicyAuditId`, which is the value to pass as `RestartCheckpoint` on the rerun.
## Related documentation
- [Architecture](architecture.md)
- [Operations and processes](operations-processes.md)
- [Operational runbook](operational-runbook.md)
## Sources
- `src/SAIF.PremiumAudit.Batch/Program.vb`
- `src/SAIF.PremiumAudit.Batch/AuditProcessor.vb`
- `src/SAIF.PremiumAudit.Batch/App.config`