An exit code is a compact, machine-readable result produced when a process terminates. A shell, build runner, container runtime, or deployment agent can inspect that result and decide what to do next. Zero conventionally means successful completion; nonzero values have meanings defined by the application or execution environment. They are not universal error categories: the same number can mean different things for different tools, and some utilities use nonzero values for expected conditions rather than operational failures.
In CI/CD pipelines, exit codes connect automated testing to release decisions. A test runner should return zero when the required tests pass and nonzero when tests fail or cannot execute successfully. The build system then converts that command result into a job or action outcome. For example, an integration test can create a test record, read it through the application API, and verify the response. Returning nonzero when the response is incorrect allows the pipeline to stop before deploying an unsuitable artifact.
Measuring application health through exit codes requires a meaningful check, not merely a command that completes. A smoke-test script might verify that an endpoint responds within a deadline, returns the expected status, and contains an expected response field. Its exit code summarizes those assertions for the pipeline. An HTTP error response does not necessarily cause an HTTP client to exit nonzero: curl normally needs an option such as --fail to treat HTTP error responses as command failures. Application-level assertions remain necessary when an endpoint returns HTTP 200 with incorrect content.
Preserving failure status is a common implementation challenge. A wrapper script that runs tests and then successfully prints a message can accidentally return the printing command's status instead of the test result. Capture the test status immediately and explicitly return it after cleanup or report generation. Likewise, a shell pipeline that sends test output through tee may report only tee's success. In Bash, enabling pipefail makes a pipeline return a nonzero status when one of its commands fails. Automatic shell termination options have contextual exceptions, so critical validation paths should use deliberate status handling rather than assume every failure will stop execution.
Cloud build and deployment services apply these principles through their execution contracts. AWS CodeBuild normally treats an unhandled failing build command as a failure, while configured failure-handling behavior can change what happens next. AWS CodePipeline consumes the resulting action status rather than directly inspecting every test process. For EC2/on-premises deployments, an AWS CodeDeploy ValidateService hook can run a post-deployment smoke test and return nonzero when validation fails. Automatic rollback must be configured where required; a failed validation does not universally imply rollback.
For long-running applications, process termination and ongoing health are different signals. An application can remain running while serving errors, and a process can exit zero after an intentional shutdown without proving continued availability. Container health-check commands bridge this gap by periodically returning a status based on an active probe. In Amazon ECS, configured health-check retries, timeouts, and start periods influence how these results become container health status.
Exit codes are inexpensive and easy to automate, but they carry little diagnostic detail. Preserve logs, test reports, and relevant metrics alongside them. On Unix-like systems, shells commonly represent signal termination using 128 plus the signal number; a value such as 137 therefore suggests SIGKILL but does not alone prove an out-of-memory event. Define documented return-code contracts, distinguish retryable infrastructure problems from deterministic test failures, and keep retries bounded so persistent defects are not hidden.