Topic 06

Yesterday's Library

Failure Modes

The agent learned from code written before its training ended, much of it older than your dependencies. So it writes calls your version has deprecated, options your version has removed, and, the other way round, features your pinned version does not have yet. Both run straight into the version you actually ship.

This is the slowest failure in the chapter. An invented method fails the day it is written; a deprecated call works, passes review because it looks familiar, and waits. When it fails, it fails at an edge: a time zone, a calendar boundary, the day the old call is finally removed.

Why the Past Leaks In

Training data is weighted by how much code used a thing. An API that was standard for ten years appears in vastly more public code than its replacement, which has been standard for two. The model's sense of "how this is done" follows the volume, so the old call comes out first.

The model's knowledge also stops at a cut-off, while your dependencies keep moving. A library that shipped a new major version after the cut-off simply does not exist in the form you use. As of 2026 many assistants can fetch current documentation while they work, which helps only when they are told to, or set up to do it without being told.

The Date Case

Date and time code is where the past leaks in most, because every language has an old date API that dominated a decade of examples. Asked to add a small piece to the cancellation screen, the agent reached for exactly that API in each language, and each line ran.

booking.py (the agent's addition)
def hours_left(booking: Booking) -> float:
    """Hours until the free cancellation ends, for the cancellation screen."""
    until = refunds.refund_deadline(booking.starts_at, booking.gym)
    return (until.timestamp() - datetime.utcnow().timestamp()) / 3600
Run on a server whose clock is set to the first zone
shown:  358.9 hours left
actual: 363.9 hours left
booking.ts (the agent's addition)
/** The deadline as a clock time, for the cancellation reminder. */
export function deadlineClock(booking: Booking): string {
  const until = refunds.refundDeadline(booking.startsAt, booking.gym);
  return until.toISOString().substr(11, 5);
}
tsc --noEmit, then the reminder for a second-zone gym
$ tsc --noEmit
$ node clock.ts
reminder: 04:00
Free cancellation until 2026-11-13 21:00
Booking.java (the agent's addition)
/** The deadline as a clock time, for the cancellation reminder. */
public String deadlineClock() {
  Date until = Date.from(Refunds.refundDeadline(startsAt, gym).toInstant());
  return "%02d:%02d".formatted(until.getHours(), until.getMinutes());
}
javac, then the reminder for a second-zone gym, on a server in the first zone
Note: src/main/java/org/cragmoor/Booking.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
reminder: 22:00
Free cancellation until 2026-11-13 21:00

The Python variant takes the current time from a call that has been deprecated since version 3.12 and returns a time with no zone attached; converted to a timestamp, that zone-less time is read as the server's local time, and the hours left come out wrong by the server's offset from UTC. The TypeScript variant cuts the time out of a date string with a method the language's own type definitions mark as deprecated, and the string is in UTC, so the reminder shows 04:00. The Java variant reads the hour from the old date type, whose field getters have been deprecated since JDK 1.1, and the hour is in the server's zone, so the reminder shows 22:00. The deadline is 21:00 in every case.

Three old date calls, each of which runs, and where each one breaks
The current time with no zone attached→Read as server time: hours left off by the server's offset from UTC
The time cut out of a UTC date string→A 21:00 deadline shown as 04:00
The hour read from the old date type→The server's zone, not the gym's: 22:00

None of these failed a test, because no test runs on a server in another zone, and none failed the type checker, because deprecated calls type-check perfectly. Each one is a time-zone bug waiting for the edges Chapter 8 tests.

Too New, Too

The reverse failure is a method that exists in the current release and not in the version your lock file pins. The agent's code is correct in general and wrong on your machine. In a compiled language you find out at build time; in a dynamic one, when the line runs.

This happens most often when the agent has been told to "use the latest approach" or has fetched the newest documentation without checking the version. The newest documentation describes a library you may not run.

The environment adds a second trap. The agent runs its checks on your machine, and your machine may have a newer toolchain than the one the build pins. A call that exists in the newer version passes every check the agent runs, then fails in continuous integration, where the pinned version is used. The agent did nothing wrong by its own evidence; its evidence came from the wrong version. Pin the toolchain in the repository, so the agent's run and the build's run use the same one.

Your Version Is the Truth

The lock file and the runtime version are the facts; everything else is recall. Name the language version and the key libraries' versions in the project instructions file, so the agent starts each session knowing them. For anything beyond the most common calls, point it at the installed package's source or the documentation for that exact version, as Chapter 4 described, instead of letting it recall a general one.

The useful lines in Cragmoor's instructions file are short and specific: the language version and where it is pinned; "dates and times use the zoned types through the helper in schedule; the old date API is not used anywhere new"; and the two libraries whose major version changed in the last year, with a link to each one's migration notes. Three lines, and the commonest version mistakes stop arriving in diffs.

Making Deprecations Loud

Most deprecation warnings are printed where nobody reads them, or not printed at all. Turn them into errors in the test run, enable the linter's deprecation rules, and run the type checker on every change, which Chapter 8 makes part of the agent's own loop. A warning the agent never sees in its own run is a warning it never acts on.

Turning deprecations into errors on an existing codebase usually produces a list of old calls on the first run. That list is not a reason to leave the setting off. Fix the calls in the modules agents touch most, mark the rest as known, and keep the setting strict for everything new, so the list only ever shrinks.

pytest -q: the warning is printed, and the run still passes
=============================== warnings summary ===============================
tests/test_refunds.py::test_hours_left_is_positive_before_the_deadline
  src/cragmoor/booking.py:32: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).

6 passed, 1 warning in 0.02s
pytest -q -W error::DeprecationWarning
E       DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).

FAILED tests/test_refunds.py::test_hours_left_is_positive_before_the_deadline
1 failed, 5 passed in 0.03s
lib.es5.d.ts, as shipped with the type checker
/**
 * Gets a substring beginning at the specified location and having the specified length.
 * @deprecated A legacy feature for browser compatibility
 */
substr(from: number, length?: number): string;
tsc --noEmit: a deprecated call is not an error, only an editor hint
$ tsc --noEmit
$ echo $?
0
javac -Xlint:deprecation -Werror
src/main/java/org/cragmoor/Booking.java:21: warning: [deprecation] getHours() in Date has been deprecated
    return "%02d:%02d".formatted(until.getHours(), until.getMinutes());
                                      ^
src/main/java/org/cragmoor/Booking.java:21: warning: [deprecation] getMinutes() in Date has been deprecated
    return "%02d:%02d".formatted(until.getHours(), until.getMinutes());
                                                        ^
error: warnings found and -Werror specified
1 error
2 warnings

The three languages differ here. One test runner prints the warning in a summary and passes until told to treat warnings as errors. One compiler prints a two-line note by default and fails only with the right flags. And one type checker treats the deprecation as an editor hint, so only a linter rule makes it fail a run. Whichever language you work in, the setting belongs in the project's configuration, where the agent's run picks it up, not in your editor.

Upgrades Go the Other Way

When you do upgrade, the agent's memory of the old version helps it read old code and misleads it when writing new code. Give it the migration guide for the versions involved, not its recollection of them. Chapter 11 covers large upgrades as a mechanical change, scripted where possible and reviewed by sample and by tests.

Misconceptions
  • "Deprecated means it does not work." Deprecated code nearly always works, which is why it ships. The zone-less current time runs fine and goes wrong only when it meets a time that has a zone.
  • "The agent knows the latest version." It knows what was common in its training data, which lags the latest release and may also predate the one you pin.
  • "The type checker would catch version problems." It checks against the installed version's types, and deprecated calls type-check perfectly.
  • "Stating the version in the prompt is enough." It helps, and recall still dominates for details. Give the agent the documentation or the installed source to read.
  • "Existing code in the repository shows the agent the right style." It shows the existing style. A deprecated call used three times in schedule gets copied a fourth time.
Why It Matters
  • Name the language version and the key library versions in the project instructions file. The agent cannot read a version nobody wrote down.
  • Run tests with deprecation warnings as errors. A deprecated call then fails the agent's own run instead of waiting for yours.
  • Point the agent at the installed version's source or documentation for any non-trivial library call. Retrieval beats recall.
  • Check new calls against the lock file, not the newest documentation. The newest documentation describes a version you may not run.

Knowledge Check

The agent's new line uses a deprecated date call. The type checker is clean and every test passes. Why did neither catch it?

  • The call still exists and type-checks, and it fails only at untested edges
  • The type checker skips any line that was written by the agent in this session
  • Deprecated calls are removed at run time, so the tests never actually reach it
  • Test runners suppress every warning unless the suite is run in verbose mode

The agent writes a call that exists in the library's current release. Your lock file pins the previous major version. Where does the failure show up?

  • In the documentation, which describes the call incorrectly for every version
  • In the lock file, which rejects calls that its pinned version does not include
  • At build time in a compiled language, and when the line runs in a dynamic one
  • As a deprecation warning, since the pinned version predates the call it uses

Which project setting makes deprecated calls visible to the agent during its own loop?

  • Turning on strikethrough for deprecated names in your own editor
  • Deprecation warnings set to fail the test run or the linter it runs
  • Upgrading every dependency to its newest release before each task
  • Asking the agent to mention deprecated calls in its final summary

The schedule module already uses a deprecated date call in three places. Why does that make the agent likelier to write a fourth?

  • It treats any call used three times as a project-approved standard API
  • Repeated calls suppress the deprecation warning for the whole module
  • The existing code is context, and it continues the style that it reads
  • The project instructions file is rebuilt from the most common calls in it

You got correct