AI has made writing code cheap. To code effectively with AI, it’s useful to learn skills commonly associated with software engineering management. That’s why we designed a curriculum around what matters most now: deciding what to build, designing solutions, and proving they’re right.
The process of development should be informed by judgment about what’s worth building and guided by building in shippable increments.
As building becomes inexpensive with AI, considering what’s worth building and what defines a good result is the first step required to avoid generating slop.
Incremental, verifiable development is necessary to maintain understanding and coherence over work that is increasingly produced by AI.
Judging an idea begins with relevant information.
Most of the information available makes no difference to a given decision. What matters is finding the few facts that would actually change it.
Example
Whether to build a feature depends on who needs it and how often, not on how interesting it would be to build.
The credibility of a source is judged, not assumed.
That something has been written, or built, says nothing about whether it is right or well done. Claims that sound confident and look plausible may be wrong regardless of where they come from.
Example
A quoted figure is worth only as much as its source and evidence. A number repeated widely can still have nothing behind it.
The assumptions behind a decision are stated and tested.
Unspoken assumptions are where decisions go wrong unnoticed. Stating them turns a guess into something that can be checked cheaply before it becomes expensive.
Example
The assumption that users want a feature can be tested with a small trial before the feature is built on top of it.
The domain the problem belongs to has its own terminology, constraints, and failure modes that must be learned.
AI reflects back what you already know. Without working familiarity with a domain — its vocabulary, its common pitfalls, its standard approaches — you cannot tell whether an answer is right, ask the right follow-up, or push back when it is wrong.
Example
A developer building a billing system who learns how invoicing, tax jurisdictions, and payment reversals work can spot errors an AI produces confidently. Without that knowledge, the output looks complete and correct.
A solution is judged against the real goal behind a request.
The goal behind a request is not always what was literally asked for, and building exactly what was asked may miss the reason for asking. A solution can work perfectly and still miss the point, because the literal request and the goal behind it are often different things. That goal may be a user need, a business outcome, or both.
Example
A request for a dashboard is really a request to support a decision. A dashboard no one uses to decide anything has answered the request and missed the point.
A good outcome is described by the qualities that matter to the people who use it and the business that supports it.
Describing success by what was hard or clever to build measures the wrong thing. The qualities users feel (speed, reliability, clarity) and the properties the business needs (maintainability, cost, measurability) are the ones that count.
Example
For a note tool, opening instantly, never losing a note, and searching quickly matter to the user. Being maintainable and cost-effective matters to the team. How clever the syncing is matters to neither.
A successful outcome is shaped by the domain the problem belongs to.
What counts as correct, fast enough, or safe enough depends on the field. A medical system and a social app have different thresholds for failure, different regulatory constraints, and different definitions of a good result. The domain sets these, not the builder’s preferences.
Example
A financial system that rounds currency incorrectly by a fraction of a cent is broken, even if every other behavior is perfect. The domain defines that precision requirement.
The value of an idea is weighed against the effort it takes. Value can be practical, educational, or personal.
A payoff does not have to be money. Something built to learn or for its own sake has real value. The question is whether it is worth the effort.
Example
A project done because it teaches you something is worth doing even if it is never released.
Choosing to spend effort on one idea gives up what that effort could have done instead. This is its opportunity cost.
The cost of a choice includes what it displaces. The best alternative given up is part of the price. A task that looks cheap can be expensive if a better use of the time exists.
Example
Time spent polishing one tool is time not spent on a more valuable tool, or on learning something new.
How much certainty to require before committing depends on how easily the decision can be reversed.
Reversibility, not importance, sets how much confidence is needed. A choice that is easy to undo can be made on a hunch, while one that is hard to undo deserves evidence first. Treating every decision as equally weighty slows down the easy ones and rushes the hard ones.
Example
Trying a layout you can undo needs no study; choosing a data format that everything will depend on deserves more.
A cheap test buys information before a full commitment is made.
Uncertainty can be reduced by spending a little to learn, instead of only thinking harder. A small trial turns a guess into evidence at low cost.
Example
A rough prototype that tests the riskiest assumption gives real information before the whole system is built.
Choosing to wait, or not to build at all, is a legitimate result.
The point of learning cheaply is to decide well, and the right decision may be to wait for a missing piece or to stop. Declining to build is a decision, a decision in itself.
Example
A trial that fails can rightly lead to dropping the idea, just as a trial that succeeds leads to building it.
Each step is a working whole from end to end, not a disconnected piece developed on its own.
Pieces developed separately and joined only at the end hide the risk of joining them until it is expensive. A thin slice that works end to end brings that risk out first. Working here means usable, not finished.
Example
A chat app that can send and show a single message from end to end is worth more than a finished database, interface, and network layer that have never run together.
The full cycle of specifying, implementing, and checking is run on a small slice before the scope grows.
Growing the scope before checking the slice piles up unchecked work. Running the whole cycle on something small keeps every step confirmed as it grows.
Example
A fixed list shown and checked before storage is added means the display is known to work before storage complicates it.
Steps are ordered so that each one can be checked on its own.
An order that produces nothing to check until the end pushes all the learning to the most expensive moment. Steps that can each be confirmed return information all along the way.
Example
Getting ‘add an item’ working and checked before implementing ‘remove an item’ confirms the base the second one needs.
The riskiest unknowns are tackled early, when there is still time to act on what is learned.
Risk put off is risk that grows. The assumption most likely to be wrong is worth testing before other work depends on it.
Example
Confirming that an uncertain outside service works before developing the features that depend on it avoids having to discard those features.
Increments are sized so that each one can be understood before the next is begun.
The right size for an increment is set by what can be understood, not by what can be generated. AI produces work faster than a person can evaluate it, so the gap between what exists and what is understood keeps growing. Smaller increments are the main way to keep that gap manageable.
Example
A developer who adds three AI-generated features without understanding any of them has a system that passes its checks but that no one can diagnose when the fourth feature breaks something in the first.
Understanding each increment means being able to explain what it does, why it was done that way, how it connects to the rest, and what would break if it changed.
These are ordered by depth. Describing what something does is the start. Explaining why it was built this way goes further. Predicting what breaks if it changes is the deepest level, and the one most often skipped. The gap between ‘the checks passed’ and this kind of understanding is comprehension debt. Testing that depth means asking about specifics: the data model, the interfaces, the assumptions, the failure modes.
Example
Understanding a payment feature means knowing why it stores card tokens rather than numbers, how it calls the external service, and what happens when the service is down. It also means knowing that it assumes a single currency.
Increments are designed around what can be reviewed, in addition to what can be generated.
The pace of generation now exceeds the pace of review. When increments outpace the ability to review them, the review becomes superficial and the understanding it provides is lost. Matching increments to review capacity keeps each one genuinely checked.
Example
Generating five features in parallel but reviewing only two leaves three unchecked. That debt compounds when the next round builds on work no one understood.
The structure of the solution reflects the structure of the problem it addresses.
A solution organized around the problem’s key concepts remains navigable. When the solution’s structure diverges from the problem’s, the system becomes harder to reason about even when each part works correctly.
Example
An order system organized around orders, items, and sellers is navigable. The same system organized around database tables, API routes, and background jobs hides the problem it solves.
Each increment preserves the coherence of the whole, in addition to the correctness of the new part.
When adding features is inexpensive, a system can grow in unplanned directions. Each addition may be correct on its own while the whole loses its design integrity. Building cost enforced this naturally; now that building is cheap, it has to be enforced deliberately.
Example
A tool that gains a chat feature, a dashboard, and a notification system (each quickly added) may work but no longer has a clear identity or a design that holds together.
Version control keeps working states so a change can be undone.
The freedom to try a risky change comes from being able to return to where you were. Without a saved point, every change is permanent, and caution replaces speed.
Example
A commit made before a large automated change turns undoing it into a single step instead of a manual reconstruction.
Small changes are easier to review, understand, and undo than large ones.
The size of a change drives both how well it can be reviewed and how far a mistake in it spreads. A small change that breaks something is easy to find; a large one hides the cause.
Example
One feature per change lets a later problem be traced to it, where a change of two thousand lines hides which part caused it.
Every piece of work moves through specification, building, and verification. As AI takes on more of the building, human skill concentrates in specifying well and verifying thoroughly.
When an AI performs the implementation, human skill concentrates in framing the problem and defining success, including how it will be verified, before anything is built.
As more work is delegated to AI, building turns into directing, adjusting when necessary, and designing workflows.
Because generation is inexpensive and pervasive, verification is the new development bottleneck. Value concentrates in detecting and correcting output, especially when it’s “almost right.”
How users will interact with a system is part of its specification, not something added after the inside is built.
As more of the implementation is automated, the design of the experience is a larger share of what requires human judgment, and it drives requirements inside the system. Whether the user can undo a mistake, for instance, determines whether the system must keep a history of edits.
Example
Choosing that a user types and sees results update live, and can undo a mistake, requires the system to keep a history of edits. This need is invisible if the interaction is treated as a final layer.
The design accounts for the mistakes a user will make, in addition to the path they are meant to follow, to a degree appropriate to the stakes.
Real use includes misuse. A design judged only on the intended path fails where users actually are. How much to invest in handling error cases depends on the consequences: hospital software and a small mobile game have different tolerances for failure.
Example
A form that prevents and forgives a mistyped date serves users better than one that silently drops it. How much protection to build depends on the cost of getting it wrong.
The data model (the things that exist, their attributes, and how they relate) is a deliberate design choice.
The first step is identifying the real concepts in the problem and choosing which attributes and relationships to represent. Domain knowledge determines which concepts are real and which are artifacts of the implementation. These choices shape what the system can express and how naturally it does so. A data model that mirrors the structure of the problem is easier to extend and reason about than one that mirrors the structure of the code.
Example
An order system that models orders, items, and sellers as separate things with clear relationships supports a multi-seller order naturally; one that flattens everything into a single record does not.
The data model is costly to change once code and stored data depend on it.
A change to behavior can often be made in one place, but a change to the data model reaches everything that reads or writes the data, and the data already stored has to be migrated too. Designing it carefully at the start avoids having to change many parts at once.
Example
Deciding late that an order can contain items from several sellers, after the design assumed one seller per order, forces changes to storage, checkout, and reporting at the same time.
A problem is broken into parts that each have one clear responsibility.
A good division gives independence: a change to one part should not force changes to the others, and each can be worked on and checked on its own. The purpose of decomposition is to reduce unintended dependencies between parts. Dividing by what looks similar instead of by what changes independently does not achieve this.
Example
A quiz app divided into question bank, scoring, display, and storage lets scoring change without changing storage; dividing it only into ‘front end’ and ‘back end’ does not.
Parts interact through a contract that hides the details most likely to change.
The contract is the promise the rest of the code depends on: what a part takes in, what it gives back, and what it is responsible for. A changeable detail such as a data format or a database choice is kept behind the contract, so that changing it stays local. Once that detail leaks through, changing it forces changes everywhere.
Example
A storage part that offers only ‘save a record’ can have its database swapped later; if it exposes database queries, everything that uses it is tied to that database.
When several parts repeat the same operation, they should share a focused module, not a general-purpose container.
A grab-bag of shared utilities has no cohesion and becomes a dependency everything must carry. A properly named module with a single, clear responsibility gives the same reuse without the hidden cost.
Example
Three modules that each format dates the same way need a date-formatting module, not a ‘utils’ file that also holds string helpers, retry logic, and logging shortcuts.
A dependency exists when one part relies on another (for data, for behavior, or for a shared decision).
One part calling another is a direct dependency. When A calls B and B calls C, A depends on C indirectly. The subtlest kind is hidden: two parts relying on the same undocumented assumption without either one declaring it. Recognizing which kind is present makes the effects predictable.
Example
A checkout depends on a payment service directly, on the payment provider’s API transitively, and on an assumed currency format that no one documented. The last is a hidden dependency.
Desirable dependencies point toward stable abstractions. Undesirable ones tie parts to details likely to change.
Not all dependencies are equal. Depending on a stable, well-defined interface is fine. It holds steady even when the internals behind it change. Depending on a volatile implementation detail is the problem, because changing that detail forces changes everywhere. The design question is which direction the dependencies point.
Example
Code that depends on a ‘send notification’ interface survives a change from email to push notifications; code that depends directly on the email library does not.
How far a change spreads is set by the dependency structure, not by how large the edit is.
A small change to something many parts depend on can force changes throughout, while a large rewrite hidden behind an unchanged interface reaches nothing else. The dependency structure is visible in the design before any code runs, so the reach of a change can be predicted.
Example
Renaming a field in a shared data format affects every feature that reads it; rewriting the internals of one service behind the same interface affects nothing outside it.
A single point of failure is a part whose failure brings down the whole system.
A single point of failure can be seen in the design before any code runs: anything every request must pass through is one. Testing each part on its own can pass while the whole system still has one.
Example
If every request depends on one cache with no backup, that cache failing takes everything down, no matter how well each feature works on its own.
The information relevant to the work is the code and data it must read or modify.
Some of that information is only read for context while some is modified, and both mark the reach of the work. When the relevant information is left unclear, the work can spread into unrelated parts of the system and cause unintended effects.
Example
Fixing a bug in an online store’s checkout may require reading the payment code and its recent history and modifying a single function; the rest of the store’s code is not relevant.
A broad, loosely specified request can change more than intended, or change the wrong thing.
The risk is highest when both the scope and the request are vague. A precise request over a large system is usually safe. A vague request over a small piece is easy to undo. The risk is highest when a vague request is aimed at a whole system.
Example
A request to ‘add a low-stock warning when fewer than five items are left,’ aimed at a whole online store, could add the warning everywhere or in the wrong place. Naming the one page it belongs on fixes it; so does spelling out, if it is wanted on every page, exactly where and how each should show it.
A specification states the intended behavior, is examined for gaps, and records the reasoning behind decisions.
Without concrete behavior statements, a builder will produce a plausible substitute, not the intended result. How much detail to give depends on the stakes. The gaps are usually in categories people forget to specify: behavior under load, on failure, at scale, or across permissions. Domain knowledge reveals these gaps. Recording the reason for each decision keeps it from being reversed by accident in a later rewrite.
Example
‘Limit login to five attempts per minute per address, then lock the account for fifteen minutes’ fixes the behavior; ‘make login more secure’ does not. A note explaining that locking is by address rather than by account, to prevent deliberate lockouts, keeps a later cleanup from undoing it.
The constraints a solution must respect are identified alongside the behavior it must produce.
Constraints include limits on time, space, cost, permissions, compatibility, safety, and business targets (budget, timeline, or revenue) that the solution cannot violate. They bound what counts as an acceptable solution in ways that behavior alone does not state, and omitting them leads to solutions that work but violate real limits.
Example
A search feature must return results in under 200 milliseconds. A feature that must ship before a pricing change takes effect has a business constraint that shapes the approach as much as any technical one.
Project-wide standards for code quality, conventions, and practices are specified once, not restated with each task.
When work is delegated, unstated expectations are filled in by the worker’s defaults, which may not match the project’s. Capturing these standards in a lasting form lets every task inherit them without restating them.
Example
A project document stating the naming conventions, error-handling patterns, and forbidden shortcuts keeps every delegated task aligned without including those rules in every individual request.
Domain-specific constraints must be identified through domain knowledge, not guessed.
Regulatory requirements, safety thresholds, and compliance rules are invisible without familiarity with the domain. Domain knowledge reveals the gaps that generic thinking misses.
Example
A healthcare app must comply with data privacy regulations that a developer unfamiliar with the field would never think to specify. The constraint is real whether or not anyone states it.
Success is stated in terms that can be observed and checked before the work begins.
A goal that cannot be observed cannot be handed off or confirmed. Turning ‘it works’ into a condition someone else could check makes the work possible to delegate and to verify afterward. Success may include technical conditions (the sort is correct) and product conditions (users complete the flow).
Example
‘Sorting returns the items in order and keeps the same count’ can be checked. ‘Users complete checkout within three steps’ is a product success condition alongside the technical ones.
Checks are defined with the specification, not after the work is built.
Designing the checks alongside the specification reveals requirements that cannot actually be tested as written. The inputs to try, the expected behavior, and what counts as failure are all decided now and carried out later, during Verify.
Example
Planning, up front, to test an empty list, a list with duplicates, and an already-sorted list shapes the specification before any code exists.
Success conditions are defined for each modular component, in addition to the system as a whole.
A system verified only at its outer boundary hides which part failed when something goes wrong. Defining what must be true of each component makes failures local and diagnosable.
Example
Specifying that the scoring module must handle ties and that the display module must truncate long names catches problems in each, rather than discovering a wrong result at the end with no idea where it came from.
How the system behaves on failure, at its limits, and under stress is part of the specification.
A specification that covers only what should happen when things go right leaves the most dangerous behavior undefined. Empty inputs, boundary values, concurrent use, and hostile input are where failures cluster, and specifying the expected behavior for these cases is part of the verification design.
Example
Specifying that a file upload must reject files over 10 MB, return a clear error on zero-byte files, and remain responsive during processing prevents discovering these requirements in production.
At least one check exercises the whole system from end to end, in addition to its parts.
Parts that each pass their own checks can still fail when connected. A check that runs the whole system finds the problems that appear only when the pieces work together.
Example
Confirming that a given input file produces the expected output file catches wiring mistakes that separate tests on each stage miss.
Deciding what to move from short-term working memory into lasting storage is part of the work.
Working memory is limited. It holds only what is active during the current session, so anything that must outlast the session has to be saved on purpose. Treating working memory as if it were permanent is how knowledge is lost and worked out again from scratch.
Example
A convention explained again at the start of every session lives only in working memory. Written down once and loaded each time, it lasts.
When accumulated context does more harm than good, starting fresh from the current state can work better than continuing.
Past a point, working memory carries its own wrong turns forward as if they were settled fact, and adding corrections only deepens the confusion. Knowing when to clear it and start over from what is actually true now takes judgment.
Example
After several failed attempts guided by an earlier misdiagnosis, starting over from the current state of the system often works where continuing the tangled thread does not.
Knowledge and steps that come up repeatedly are captured in a reusable form (e.g., a written procedure, a template, a script) so they are not worked out again each time.
The first time is discovery; by the third time, redoing it is waste. Capturing the steps once turns effort spent into effort saved every time after. It also makes the knowledge something others can use, beyond one person’s memory.
Example
A checklist run before every release, or a script that sets up a project, replaces trying to remember the steps and getting them slightly wrong each time.
Existing reusable knowledge is used and improved rather than rebuilt from scratch.
Captured knowledge only pays off if it is actually used and kept up to date. Rebuilding what already exists wastes effort and lets the captured version fall behind. Improving the shared version helps everyone; keeping a private copy does not.
Example
Improving the shared setup script when it falls short, instead of writing a personal one, keeps a single correct version for everyone.
Directing an agent well means stating the goal, the information the work needs, and the limits it must stay within.
An agent fills in whatever is left unsaid, so stating what it must not change matters as much as stating the goal. Domain knowledge makes the difference between a vague goal and a precise one. This is the same judgment about relevant information from SPC-5, applied at the moment work is handed off.
Example
An agent given the specification, the two files involved, and an instruction to leave the shared interface alone stays on task; given only the goal, it may change that interface.
Directing an AI agent well requires domain knowledge to judge and correct its output.
An agent fills in whatever you leave unsaid with plausible defaults. Domain expertise is what lets you recognize when those defaults are wrong, ask precise follow-up questions, and catch errors that look correct on the surface.
Example
A developer with database experience who directs an agent to design a schema can spot that the suggested approach will break under concurrent writes. Without that expertise, the suggestion looks complete.
New information discovered during implementation (a broken assumption, a missing requirement, an unexpected difficulty) is a signal to stop and revise the specification before continuing.
Continuing on a specification that has been shown to be wrong compounds the error. Recognizing when to stop, correct the specification, and restart is harder than patching around the problem, but it leads to a better result.
Example
Discovering that an external service returns data in a different format than assumed is a reason to revise the specification and the data model, not to add conversion code throughout.
When restarting delegated work after a significant revision, the new context comes from the current state, not the history of the failed attempt.
Carrying forward the full history of a wrong approach pollutes the restart with stale assumptions. The new attempt begins with the current known state and the revised specification.
Example
After discovering a design flaw three steps into a delegated piece of work, restarting with a corrected specification and the current state of the system produces better results than asking the worker to undo its last three steps.
Work can be shared among several workers when it divides into parts that can proceed independently.
Seeing where the work divides is distinct from managing the workers who carry it out. The division itself is about the shape of the work.
Example
Seeing that an interface, its tests, and its documentation can be developed independently means all three can be given to separate workers instead of done one after another.
A small but crucial piece of human work can make the rest safe to delegate.
Good delegation separates the parts that need human judgment from the parts that do not. Do the specification, the key decision, the hardest case. Then hand off the rest.
Example
Writing the specification and the two hardest test cases, then delegating the rest of the implementation, keeps the judgment where it belongs; handing off the specification itself would give it away.
Independent parts can proceed at the same time; parts that depend on a shared decision or on each other’s output proceed in sequence.
Work that uses another’s output can often still proceed in parallel against an agreed interface. Using another’s result is not by itself a reason for sequencing. What forces an order is a decision not yet made or an output not yet produced.
Example
Two services that call each other can be developed at the same time once they agree on how they exchange information; only the shared decision before them and the joining after them happen in sequence.
Work is organized into tasks with clear ownership and clear hand-offs.
What lasts is the design of the workflow: who does what, and what passes between them. Unclear ownership and vague hand-offs cause the same failures at any scale.
Example
Dividing a feature into developing the interface, writing the tests, and writing the documentation, with a clear hand-off between them, works the same way at any scale.
Tasks are ordered by their real dependencies, while independent tasks run at the same time (see BLD-3).
A task waits only on what it truly needs. What has to happen in order is agreeing a shared interface first and joining the results last; the work between can often run at the same time.
Example
In a three-stage pipeline (gather, transform, load), the stages must wait on the one before them, but gathering data from three different sources can happen at the same time.
Tasks that require the same mental model are kept together rather than split across workers.
Splitting by task type (all tests in one batch, all fixes in another) forces each worker to build a separate understanding of every part it touches. Grouping by the shared understanding needed lets each worker go deep on one area, and keeps the context that would otherwise be scattered.
Example
Keeping ‘fix the API and write its tests’ as one task works better than separating ‘all fixes’ from ‘all tests,’ because the fix and its test share the same understanding of what the API should do.
A workflow marks which steps need review before continuing and which can proceed without it.
Not every step needs oversight, and not every step can safely go without it. Placing the review points on purpose puts limited attention where a mistake would be expensive or hard to undo.
Example
A workflow that lets a worker draft freely but requires approval before anything is merged puts the review where the risk is.
Conflicting results are resolved by what the combined result must satisfy, not by manual merging.
As work is split further, joining results by hand does not scale. Stating what must remain true of the whole and reconciling the pieces against that works where manual merging does not.
Example
When two workers change the same behavior, stating what must stay true of the result and reconciling to it works better than stitching their changes together by hand.
When work is automated, one coordinator divides the work, sends out the independent parts, and joins the results.
The same dividing, ordering, and review points apply whether the work is done manually or automatically. Because automated workers can fail without reporting it, the review points and checks matter more, not less.
Example
A coordinator that splits a task, delegates the parts, and gathers the results needs the same workflow design as a team lead, with a stronger need to verify each result.
Correctness (whether the code produces the intended result) is the base level of verification.
Every other level of verification assumes correctness. A program can be well organized and fast and still be wrong, so correctness is necessary but not enough on its own.
Example
A sort that returns the items in the wrong order fails here, however clean or fast it is.
Quality (how readable, changeable, and coherent the code is) is a level beyond correctness.
Correct code may be difficult to read, change, and maintain. Code is usually changed more often than it is written, so quality decides the cost of the next change, not this one. Poorly structured code also costs more to work with using AI tools. Clean architecture pays off in both human effort and AI efficiency.
Example
Code that works but repeats the same logic in several places is correct today and expensive on the day that logic has to change. An AI working on a poorly organized file can also consume several times the resources it would on a well-structured one.
Output and behavior (what the system does, shows, and feels like to use) is checked beyond the code itself.
Reading the code is not the same as using the system. The visual layout, the interactions, and the experience of using it can each be wrong even when every function works correctly.
Example
A report whose calculations are correct can still show misaligned columns, confusing labels, or an interaction that leads users to make mistakes.
The data model and how data flows through the system are verified against the specification.
The structure of the data and the way it is transformed from input to output can diverge from what was intended even when each step works correctly on its own. Checking the data model and its flow catches mismatches that isolated checks on individual functions miss.
Example
A pipeline where each stage works correctly on its own can still lose a field, change a format, or silently drop records between stages.
Performance refers to the time and memory a system uses. Correct, well-built systems should still be checked for performance.
Performance should meet what is expected for the load the system is meant to handle. A prototype or early increment does not need to be fast, but a system serving many users must be checked under realistic conditions.
Example
A query that works on ten rows may be unusable on ten million. Checking performance means testing at the scale the system will actually run at.
Which dimensions matter most depends on the domain.
Domain knowledge determines which verification dimensions to prioritize. A trading system demands latency checks that a blog does not; medical software demands correctness guarantees that a game does not. Without domain familiarity, the wrong dimensions get the attention.
Example
A healthcare app verified for speed and visual polish but not for data accuracy under concurrent edits has checked the wrong things first.
Automated checks confirm expected behavior again each time the code changes.
How much to invest in automated checks depends on how long the code will last and what a failure would cost. A check earns its keep when the code changes often and a mistake would be expensive. Whether the stakes justify the effort determines how much to invest.
Example
A script run once may be checked by eye; a billing calculation earns checks that run on every change, because a quiet mistake charges real customers.
Mistakes are found by testing the inputs where programs actually break (empty, extreme, duplicated, and hostile).
Most failures hide at the boundaries, not in the ordinary case. An empty list, a single item, a maximum value, a duplicate, and a deliberately wrong input find problems that testing the expected case never will. Domain knowledge reveals which boundary cases matter most.
Example
A function tried only on a short, sorted list can still mishandle an empty list, a single item, or duplicates. These are the cases people actually run into.
An invariant is a condition that must hold for every valid input, whatever the specific case.
Stating a property that has to be true for all inputs checks a whole class of cases at once, rather than one example at a time.
Example
A sort must always return the same number of items it was given, in order. This property holds for every possible list.
Verification rests on evidence that can be seen, not on a claim that the code works.
‘It works’ is a statement, not a check. A run that passes, a saved result, or a screenshot lets someone else, or a later self, trust the work without repeating it.
Example
Showing the case that used to fail now passing is convincing in a way that ‘fixed it’ is not.
Reading unfamiliar code begins with working out what it is meant to do, before judging or changing it.
Code shows what happens, not why. Changing it without recovering the intent risks removing something that was doing necessary work but looked unnecessary. This risk grows as more code is read than written.
Example
A check that looks pointless may be handling a case the author met in production; removing it without knowing why brings the problem back.
Reviewing is more effective when it focuses on what changed and runs that path.
Attention is limited, and spreading it evenly across a large change misses the few risky lines. Focusing on what actually changed, and running that path, catches more.
Example
In a large change, reading the difference and running the one new path finds more than skimming every line.
When an abstraction leaks, understanding the behavior means looking at the level beneath it.
A higher-level view is enough until it is not. A problem that appears only under real conditions often needs knowledge of the layer the abstraction was hiding (e.g., the network, the database, or memory).
Example
Code that is fast on a laptop but slow in production is explained by network and database behavior the laptop run hid.
Instrumentation and logging make a system’s behavior visible instead of assumed.
A behavior that cannot be seen cannot be fixed with confidence. Adding logging turns ‘it seems slow’ into a specific, located cause.
Example
Logging how long each request takes points to the slow step that guessing had blamed on the wrong place.
Signal from the running system shows whether a change actually worked, both technically and for the business.
Evidence from real use is the final confirmation that a change had the intended effect. Technical metrics (error rates, latency) confirm it works; product and business metrics (usage, conversion, retention) confirm it matters. Both kinds of signal often reveal the next thing to fix.
Example
A drop in the error rate after a fix confirms it technically. A rise in the completion rate confirms it helped users. The errors and drop-offs that remain show what to look at next.
A defect is reproduced reliably before it is diagnosed.
A problem that cannot be triggered on purpose cannot be confirmed as fixed. Finding a consistent way to trigger it is the first step, not a formality.
Example
A failure that seemed random happens on inputs ending in .5; once that is known, it can be worked on rather than guessed at.
Locating a defect narrows it to a specific place before anything is changed.
A problem usually shows up after the point where it started, so changing the code where it appears often treats the symptom. Tracing or narrowing down finds where a value first becomes wrong.
Example
A wrong total on the screen may come from a calculation several steps earlier, not from the code that displays it.
A suspected cause is tested before the code is changed.
Changing code on a hunch can hide the symptom while leaving the cause in place, or add a new problem. Testing the guess first confirms it is really the cause.
Example
Turning off a suspected rounding step and watching the problem disappear confirms the cause before any fix is written.
Confirming a fix is a separate step from finding the cause.
Finding what went wrong and correcting it are different actions, and a correction is only trusted once it is checked. Confirming means running the original trigger and the surrounding checks again.
Example
After the fix, the case that failed and the full set of checks are run again to make sure it holds.
An agent can be given tools to check its own work and show evidence, beyond reporting success.
Asked only to describe its work, an agent will report a success it cannot actually see. Giving it a way to observe the outcome (take a screenshot, run the checks) makes the result something that can be verified.
Example
An agent able to control a browser can capture the finished page and show the change is really there, instead of stating that it made the change.
A self-checking loop is given limits and stopping conditions, like any safe loop in code (see ABS-3).
A loop that builds, checks, and fixes with no way to stop can run without end or swing back and forth. The reasoning that keeps a loop in code from running forever (a limit, a guard, a stopping condition) applies to the workflow just the same.
Example
A loop that keeps trying until the checks pass, but stops after a set number of attempts, avoids running forever on a problem it cannot fix.
An agent given broad tools is kept safe from the combination of private data, untrusted content, and a way to communicate outward (see SEC-4).
Access to private data, exposure to untrusted content, and a way to send data out are each harmless alone. Together they form a path by which untrusted content can direct the agent to send private data out, and removing any one of the three breaks the path.
Example
An agent that can read secrets and browse the web is either kept away from untrusted pages or given no way to send data out, so a harmful page cannot use it to leak.
A rubric turns a general sense of ‘good’ into criteria that can be scored.
Until ‘good’ is broken into things that can be observed, quality judgments cannot be repeated or handed off. Without a rubric, the same work gets different grades from different reviewers, or from the same reviewer on different days.
Example
Scoring a summary on faithfulness, completeness, and length gives comparable results where ‘is it good?’ only starts an argument.
A good metric cannot be satisfied without the real improvement it stands for.
A metric that becomes a target invites improving the number instead of the thing. A measure remains reliable when it cannot improve without the real quality improving too. This applies to business metrics as well: a metric like pages visited can rise while user satisfaction falls. The metric should track the outcome that matters, not a proxy for it.
Example
Measuring how much of the real logic the tests cover is harder to game than counting how many lines of tests there are.
An evaluation scores a system against representative inputs paired with the qualities a good answer should have.
A few casual tries prove little; a prepared set, scored at scale by a rubric or a judging model, measures behavior in a repeatable way. The borderline cases (where a model and a person would disagree) are left to human review.
Example
Fifty representative questions with reference answers, scored automatically, catch a slip that spot-checking would miss, while the unclear few are read by hand.
Security and abstraction are lenses to apply throughout the working process. They shape how every specification is written, every build is checked, and every system is reasoned about.
AI expands the attack surface and readily produces insecure code; verifying safety, in addition to correctness, spans design, build, and verification.
Tools change continually; the ability to move between levels of abstraction and to re-apply reasoning at a higher one is how competence lasts across tool changes.
A system is examined for how it can be misused or attacked, in addition to whether it works as intended.
Checking that the intended path works says nothing about what someone hostile can do with the paths that were not intended.
Example
An upload feature that works for ordinary files still has to be examined for files that are too large, of the wrong type, or able to run as code.
Input from outside is treated as hostile until it is checked at the boundary where it enters.
The boundary where input enters is the point where data from outside crosses into the system. Checking it there, rather than assuming good intent, keeps a hostile input at the edge instead of deep inside.
Example
A filename coming from a browser is checked, and an attempt to escape the intended folder is refused, before any code acts on it.
Each part is given the least access it needs to do its job.
Access granted ‘to be safe’ is access an attacker gains if that part is taken over. Keeping it to the minimum limits how far a single break can spread.
Example
A reporting service given read-only access cannot be used to change data even if it is taken over.
Injection is prevented by design, using interfaces that keep instructions separate from data.
Filtering and escaping input are patches that are easy to get wrong. An interface that cannot mistake data for instructions removes the whole class of problem instead of handling it case by case.
Example
A parameterized query, which keeps the values separate from the command, makes hostile input impossible to run as SQL; building the command by pasting the values in invites it.
Secrets are kept out of the source code, in a store meant for them.
A secret in the code is a secret in its history, its backups, and every copy. Keeping it separate means it can be changed or revoked without changing the code.
Example
An access key read from a secret store can be replaced without a code change, unlike one pasted into the code.
AI-generated code can look right and still be insecure.
AI-generated code is reviewed for weaknesses, for security in addition to correctness, because it is shaped to look right and to work on the example, not to be safe. The common failure is confident, ordinary-looking code that misses the security-relevant case.
Example
A generated upload handler that leaves out type and size checks looks complete and passes an ordinary test while remaining open to attack.
Generated code is treated as untrusted by default and checked with security tools, not assumed safe.
The amount and fluency of generated code make reading it by hand alone insufficient. Automated scanners catch known kinds of weakness at scale, and the starting assumption is distrust until it is checked.
Example
Running an automatic security scan on a generated endpoint before it is deployed catches problems its fluency hides.
Content that an agent reads is treated as untrusted, because it can carry instructions that hijack what the agent does.
An agent that takes in outside content can be steered by instructions hidden in it. Treating that content as data to be handled, and never as commands to be followed, prevents this.
Example
An agent that reads a web page telling it to send data out has to ignore the instruction rather than act on it.
What a model produces is treated as untrusted and is not run or shown without care.
A model’s output can carry unsafe content into whatever uses it next. It is handled with the same caution as any input from outside.
Example
A model’s output is not run as code or shown as raw web content without being made safe first.
A system that acts on its own is given only the authority its task needs.
Too much authority turns a wrong decision into a costly action. The same least-access reasoning used for parts of a system applies more strongly to something that acts on its own.
Example
An agent that can read files but cannot delete data or send mail without approval cannot turn a mistake into something irreversible.
Access to private data, exposure to untrusted content, and a way to communicate outward together form a path for data to leak.
The danger is granting all three at once: access to private data, exposure to untrusted content, and a way to communicate outward. Each is safe on its own, but together, untrusted content can direct the system to send private data out. Removing any one breaks the path; this combination is known as the lethal trifecta.
Example
An agent that holds secrets and can browse the web is either kept away from untrusted pages or given no way to send data out, so a hostile page cannot make it leak.
A dependency is checked for authenticity and upkeep before it is adopted.
Bringing in a package brings in its authors and its future. An unmaintained or fake dependency is a weakness added on purpose.
Example
Confirming that a suggested library exists, is widely used, and is still maintained before adding it avoids taking on an abandoned or fake one.
Suggested package names are verified, because AI systems can name ones that do not exist or that impostors have registered.
A plausible package name from a model may be invented, and attackers register those plausible names so trusting users install them. This risk is called slopsquatting. The check costs little; the compromise does not.
Example
A suggestion to install a slightly misspelled package name is treated as a likely fake and checked before it is installed.
Reasoning is done at the level where the problem actually sits (up at intent and structure, or down at a specific mechanism).
Working at the wrong level wastes effort: fixing a line when the design is wrong, or redesigning when a single value is off. Choosing the level comes before the work.
Example
Before fixing a line, checking whether the feature was even specified correctly can make the fix unnecessary.
Working out which level a problem sits at is itself part of diagnosing it.
The same symptom can come from a typo, a mistake in logic, or a flawed data model. Naming the level says what kind of fix is even possible.
Example
A wrong result that comes from the data model cannot be fixed by editing the function where it showed up.
An interface separates what a part does from how it does it, so the how can change without disturbing the parts that use it.
An interface exists to contain change, not to look tidy. What is placed behind it is exactly what is expected to change.
Example
An interface that hides which payment provider is used lets the provider be changed without changing the code that calls it.
An abstraction leaks when the details it was meant to hide become visible to the parts that use it.
A leak removes the benefit: callers now depend on what was supposed to be hidden, so you pay the cost of the abstraction without getting the protection. A boundary on paper becomes real only if nothing leaks through.
Example
If the code that calls a storage part has to know which database is underneath, the abstraction has leaked, and changing the database reaches that code anyway.
A way of reasoning learned at one level reappears at higher levels as tools advance.
Skills last across tool changes when the underlying reasoning pattern carries over to the next level. The shape of the thinking stays the same even when the material does not, which is why fundamentals outlast any single tool.
Example
Breaking a program into functions and breaking a project into delegated tasks are the same move at different levels.
Long-established computing ideas apply to each new layer of tools, including automated workflows.
New tools do not cancel old reasoning. Invariants, dependencies, and abstraction apply to an automated workflow as much as to a loop in code, and assuming a new layer is exempt is where it fails.
Example
The idea of a loop invariant applies to reasoning about a self-checking automated workflow, as much as to a loop in code.
Brilliant is a member in the kidSAFE Seal Program. To learn more, click on the seal or go to www.kidsafe.com.
© 2026 Brilliant Worldwide, Inc., Brilliant and the Brilliant Logo are trademarks of Brilliant Worldwide, Inc.