Week 06 · lesson

Exit Codes and Logs Are Part of the Interface

A script that prints “done” is not necessarily done.

Operational tools need a way to communicate outcome to both humans and other programs.

Two common mechanisms are exit status and structured logs.

Exit status communicates success or failure

On many operating systems, a process exits with a numeric status.

By convention:

0 → success
non-zero → some form of failure

The exact non-zero values depend on the program.

This means another process can make a decision without scraping a sentence from the screen.

Example shell pattern in an isolated lab:

python3 archive_logs.py
status=$?
echo "exit_status=$status"

If you do not have a shell, use the supplied traces in this lesson instead.

“Partial success” needs a policy

Suppose a script processes ten files:

8 moved
1 skipped because it was not a .log file
1 failed because destination already existed

Should the exit code be success?

There is no universal answer. The task contract should decide.

Possible policy:

  • unexpected file type is a normal skip;
  • destination conflict is an error;
  • any error produces non-zero exit status.

The important thing is consistency between documented semantics and observed outcome.

Structured logs reduce ambiguity

Compare:

Something went wrong

with:

level=error event=destination-conflict file=alpha.log action=not-moved

The second message lets you ask specific questions.

But do not log everything merely because you can.

Avoid recording:

  • passwords;
  • API tokens;
  • private student records;
  • full secret-bearing configuration;
  • unrelated file contents; or
  • unnecessary personal identifiers.

A log is itself a data system.

Supplied execution trace

Review this fictional run:

10:02:11 level=info event=start candidates=3
10:02:11 level=info event=moved file=a.log
10:02:11 level=error event=destination-conflict file=b.log
10:02:11 level=info event=skip file=notes.txt reason=extension
10:02:11 level=info event=finish moved=1 skipped=1 errors=1
exit_status=0

The logging says an error occurred, but the exit status says success.

That is an interface inconsistency.

Do not claim data loss occurred; the trace does not show that.

A defensible claim is:

The supplied run reported one destination conflict while returning success status, so an automated caller that relies only on exit status could misclassify this run as successful.

That is precise.

Activity: define outcome semantics

Create an outcome table for your Week 6 task.

ConditionLog level/eventExit outcomeContinue?
all expected changes succeedinfo/completesuccessn/a
allowed non-target fileinfo/skipsuccessyes
destination conflicterror/conflictfailurepolicy
source root missingerror/preconditionfailurestop
unexpected root/patherror/scopefailurestop

Your exact table may differ. Defend it from the task contract.

Retries need limits

A retry can recover from a transient failure.

An unbounded retry can create a new failure.

Model:

attempt

success? ─ yes → finish

  no

retryable? ─ no → stop + report

  yes

attempts remaining? ─ no → stop + report

  yes

wait → retry

A good retry policy defines:

  • which failures are retryable;
  • maximum attempts;
  • delay/backoff;
  • stop condition; and
  • evidence recorded for each attempt.

“Retry until it works” is not a policy.

Monitoring scripts need truthful status

Imagine a monitoring job reports healthy because the process itself ran successfully, even though the check discovered the service was unhealthy.

That confuses script health with system health.

Names and outcomes should distinguish them.

For example:

check_execution=success
service_state=unhealthy

Now two different claims remain separate.

Extend your Automation Safety Record

Add:

  • outcome table;
  • success exit rule;
  • failure exit rule;
  • bounded log fields;
  • prohibited log data;
  • retry policy if relevant;
  • one supplied or local execution trace; and
  • one inconsistency you would detect.

Lesson 3 will test repeatability and recovery.