Planning the Work
Vertical Slices: A Small but End-to-End Result
How to cut a feature into small end-to-end slices that each deliver one checkable behaviour, without shrinking correctness, access control or the…
A vertical slice is a bounded piece of a feature that carries one meaningful behaviour through whichever parts of the system it needs, and lets you check a concrete result. For example: a customer sees their upcoming booking with the correct time and cannot see anyone else's. Delivering that might take a screen, a server-side access check, a data read and a test. If an existing interface already does the job, no new screen is needed; if the information already lives in the same table, a separate migration may be unnecessary too.
"Vertical" describes the completeness of a behaviour, not a required number of folders, components or services. The goal is to bring a small but important assumption to a clear, checkable state early, not to stuff a formally complete set of technical layers into every task.
Throughout this unit we use a fictional booking service. The names, dates, amounts, system responses and figures are invented for teaching; they are not facts about any real company.
After this unit you will be able to:
- tell a horizontal technical stage apart from a checkable vertical slice;
- name the user behaviour, the scope boundary and the acceptance evidence for a single slice;
- reduce a feature's scope while keeping its correctness, access control and data protection intact;
- spot technical prerequisites, shared contracts and the order of integration;
- distinguish code that is ready for review from a feature that is ready to show users;
- apply the small-batch principle to agent work without mistaking a small set of changed lines for proof of correctness.
What actually runs "vertically"
In a booking system you could draw horizontal boundaries by type of work:
- add a status field to the database;
- implement the server operations;
- draw the pages;
- wire everything together later.
Each step may be necessary, yet after the first three the customer still may not be able to do anything. The field exists, the server has a route, the screen is drawn — but there is no finished scenario that combines them so a person gets the result they need. This is the situation where "a lot has been done" is not the same as "one thing works".
In a vertical slice the team draws the boundary through a behaviour instead. For example: "The customer opens the page and sees the date and time of their next booking in a defined time zone; if there is no booking, the system says so; someone else's booking is never revealed." Such a slice may touch routing, authorisation, a database query, presentation and testing. But it does not have to change all of them: if the database already stores the data you need, schema work adds no value.
| Question | Horizontal pile of work | Checkable slice |
|---|---|---|
| What do we finish? | A layer or a technical component | A behaviour with a named consumer |
| What can be shown or checked? | Often only an internal detail | A concrete positive and negative scenario |
| What early signal do we get? | That one part compiles or passes its own check | Whether an important path works and the parts agree |
| What can the work uncover? | A bug inside a single component | A wrong assumption at the seam between components |
This table does not belittle technical work. A data schema, accessibility, backups, an observability tool or a library upgrade can be important results in their own right. The only question is whether we have named the real goal and whether the unit of work matches it.
A vertical slice is not necessarily a change to the visible interface. An isolated server operation with a guaranteed idempotent retry — one that can safely be repeated without doing the work twice — can be a finished, checkable capability for another service. Conversely, a handsome button that does not yet perform its action may be a prototype, but it is not a finished feature. The name of a unit of work should match what is actually ready.
A slice starts with a promise to the user
Before splitting work into technical steps, write one sentence: "Who can do what, under which conditions, and how do we know the result?" That is the outer edge of the slice — the behaviour someone sees or consumes. For example:
A signed-in customer sees one current upcoming booking with its confirmed time and time zone. If there is no upcoming booking, the system says so without inventing details. The customer cannot read another person's booking by substituting an identifier.
This promise contains a positive result, an empty state and a security boundary. Next, work out how the system identifies the customer and the authoritative time, what it shows in the "no data" and "stale data" states, and what evidence will confirm the access check. Questions about the components help you find the decisions you need:
- where the system determines the current customer;
- how it finds exactly that customer's nearest upcoming booking;
- which field holds the authoritative time, and whether there is a defined time zone or conversion rule;
- what the page or response looks like for the existing consumer;
- what a customer with no booking, or with stale data, will see;
- how a check will prove that substituting someone else's identifier exposes no data.
This is a search for the necessary rules and evidence, not a ready-made list of technical tasks. The field you need may already be available through a server method, so the database change disappears; or an ambiguous time field may need a separate decision about which source is authoritative. Access control and data correctness are never optional scope.
Do not add actions just because they feel "logically nearby": viewing does not imply rescheduling, payment, notifications, calendar export, admin editing or reporting. The mandatory core is the correct time, the ownership check and an honest "no booking" state; calendar export can wait. In the "Map the Scopes" part of Shape Up, the author marks optional nice-to-haves with a tilde (~). The full worked example with evidence follows in viewing your own upcoming booking.
A small slice is not the same as a small number of lines
A dozen changed lines can break a critical permission check. A long list of changes can consist of isolated translations, a necessary rename or a mechanical upgrade. The size of a change affects how hard it is to review, but it is not a measure of impact, completeness or safety.
A useful slice is small in uncertainty and in the consequences of being unfinished:
- a person can describe what already works;
- the team knows whom it helps;
- the result has a visible boundary;
- the unfinished part of the feature does not pose as finished;
- the important behaviour can be checked even if the next extension comes later.
That is what separates a small batch of work from a "small ticket". "Create a data transfer object", "draw the card" or "add a field" can be implementation steps. They become slices in their own right only when they have a distinct consumer, an operational benefit or a targeted piece of evidence about a risk. Without that, it is better to show them as parts of the parent scenario.
Which part to build first
Teams often don't know which end to start from. In the "Get One Piece Done" part of Shape Up, Ryan Singer describes his own example of a feature that gave clients access to selected materials in a Basecamp project. The team chose the visibility toggle as an early integration point: the designer tested the interaction while the programmer explored the access model in parallel, and then the toggle was wired to persistence. The author presents this as Basecamp's specific experience, not as a guaranteed order for every product.
What follows are practical questions, not a universal ranking:
- Is this behaviour central? Without it, does the target feature still make sense?
- Is its boundary small? Can the team isolate the result without faking an unfinished whole?
- Does it remove a significant unknown? For example, will the customer understand which wording means "booking confirmed"?
- Can you see a real integration? A mock-up, a schema or a temporary stub on its own can hide an important problem.
- What happens if the check does not confirm the assumption? Can we change direction before we have invested in other dependent parts?
Testing the central risk does not always mean the first thing must be a feature you can hand to users. Checking a payment provider's contract, for instance, may end in a conclusion and a decision rather than a button. To choose the first step, it is more useful to know which assumption could change the direction of the work than to aim mechanically for a demo screen.
Shape Up distinguishes a coherent piece you can show from a list of partly finished items, and it warns against waiting for one big integration at the end. The author's remarks about demos should not be read as a requirement to show every feature to external clients. A demo to the team, a test run, a limited internal deployment and a public launch are different events.
Reduce scope, not guarantees
You can narrow scope and still keep the behaviour correct. You can support only one type of user at first, one operation, one channel, a simple rule or a limited range of data. What matters is stating clearly what now works, for whom, and under which conditions.
For the booking service, possible limits include:
- show only the next upcoming booking, not the whole history;
- show customers their own data first, rather than building an admin panel;
- support one agreed display time zone, if the system's rules allow it;
- postpone calendar export, automatic reminders and filters;
- reuse the existing authentication mechanism instead of building a new way to sign in.
This limits the target feature. It does not permit skipping the ownership check, hiding an unconfirmed date or handing out personal data through an unchecked parameter.
Two ways to "simplify cancellation"
Safe narrowing. At first, cancellation is allowed only to the customer, only for their own booking, only when its status is "confirmed" and only when it starts more than 24 hours from now. The team postpones cancellation by staff, rescheduling, refunds and the exception for urgent cases. The server checks the owner, the current state and the time limit; when two requests race each other, exactly one of them changes the record correctly. The customer sees the final state, and the result is shown only after the change has been saved.
Unsafe trimming. To get the button out faster, the team hides it in the interface for other people's bookings, but the server accepts a substituted identifier and changes any record. A visual restriction is not access control. Likewise, you must not record an action as successful — or show success — if the database has not confirmed the change.
An MVP (minimum viable product) is a limited way to test a hypothesis, not a general licence to drop correctness checks. A small result must still be minimally fit for the promise it makes. If the system cannot yet support safe cancellation, an honest first result may offer only viewing plus a way to contact staff, rather than an imitation of cancellation. The boundaries and repeated requests are worked through in detail in cancellation without a button that only pretends.
Independent scopes, not a split by specialism
In "Map the Scopes", Shape Up advises dividing work along meaningful boundaries and real dependencies, not by roles such as "design", "server" and "database". A scope is useful when it can be finished on its own and you can say plainly what it changed. For cancelling a booking, you might separately clarify the rule and agree the contract between the server and the interface; "rework all the models", on its own, names no finished result. Likewise, "visibility of one type of notification for the customer" can be a meaningful scope, while "all data models" is a technology category, not a user outcome.
Not every scope needs its own ticket number, and the split itself does not define a queue: the server and the interface can be prepared in parallel around a shared contract. Sometimes one team changes the server and the page together in a single slice; sometimes a common contract and a shared migration tie together pieces of work that technically touch different directories. Decomposition is a way to make dependencies and results understandable, not a bureaucratic demand for more tickets.
A fictional feature, "the customer cancels their own booking", might have these scopes:
- Find the rule. The product owner confirms the time limits, statuses and any effect on payment; the team records what is still undecided.
- Safe server change. For one permitted status, the server atomically checks the owner, the state and the time condition, saves the result and returns it to the client.
- Accessible customer path. The confirmed-booking page shows the cancellation terms, a confirmation prompt and the final status.
- Observable result. Within a defined policy, the team can find out how many attempts succeeded, how many were rejected and whether there were errors, without storing unnecessary sensitive data.
This is not a mandatory "server first, then interface" queue. Clarifying the rule may reveal that self-service cancellation cannot be included at all without handling refunds. The server contract and the confirmation form can clarify each other in parallel. What matters is not to declare the scenario ready until its boundaries support the end result.
Nor does the list of scopes necessarily equal a list of independent releases. They may be linked by one schema, a shared component, data or an agreement about errors. Independence of implementation does not mean independence of deployment: a shared schema, data or error format may require a coordinated release. Independence means you can finish a part without a hidden unfinished dependency — not that you can safely deploy it or give it to users in isolation. The order of dependencies is covered in more depth in dependency graphs.
Worked example: viewing your own upcoming booking
Imagine that customers phone the studio to check the time of their next appointment. This is an invented teaching motive, not a measurement of a real service. The team wants to let customers see their booking in the web application. Using the fictional booking system, let's test a slice for viewing the next booking.
Need and boundary
User outcome. After signing in, the customer can check the time of the nearest upcoming booking they are entitled to see.
In the slice:
- identifying the current customer through a proven authentication mechanism;
- finding only that customer's upcoming booking;
- showing the confirmed time with a defined time zone or an unambiguous display rule;
- a clear state when there is no upcoming booking;
- a server-side access check that holds even if the identifier in the request is substituted;
- checks of the positive and negative scenarios and of the actual data source.
Out of scope: creating, rescheduling and cancelling bookings, payment, calendar export, history and admin editing.
Minimal contract map
| Condition | Expected behaviour |
|---|---|
| The customer is signed in and has an upcoming booking | Show the correct booking and the confirmed time |
| The customer is signed in and has no upcoming booking | Show a "no upcoming bookings" state |
| The person is not signed in | Do not release private data; apply the sign-in policy the system defines |
| The customer passes the identifier of someone else's booking | Do not reveal its data; do not rely on a hidden button |
| The stored time cannot be converted under the rule | Show a controlled error and record a diagnostic fact without unnecessary personal information |
| The data is stale because of a concurrent change | Do not present the old time as the newly confirmed time; re-read the record or explain its actual state |
The team has to find out whether the route accepts a booking identifier at all. If the current server takes the owner from the session and itself selects the single permitted record, the negative substitution test may instead target another existing operation or the server-side access query. Do not add a dangerous parameter just so the example has a "substitution test".
If the time cannot be converted, the error must be controlled and diagnosable without exposing unnecessary personal data. After a concurrent change, the old time must not be called confirmed: the system re-reads the record or explains its actual state.
Implementation as a sequence of checkable evidence
First, name the source of truth for the time and the way the owner is established. Then trace one request from the active session to the record and back to the presentation. If the data access layer returns a list of all upcoming bookings and the server route expects the interface to filter out other people's, the access boundary is in the wrong place.
The data check should use two separate sets: one customer with their own booking and another owner with a separate booking. In the example, the check confirms that the first customer's session cannot read the second booking. A plain assertion that "the page returned 200" does not prove this. A page that shows someone else's data with status 200 technically responded, but it broke the outcome.
The empty-state check must make sure an empty list is not shown as an error that never happened. The response to the user and the diagnostic log can differ: the text shown outside must not reveal SQL (Structured Query Language), other records or an internal token.
In the end the first slice really can be small: "see my next booking". It does not promise a complete booking system. It is finished for the behaviour it declares, and it leaves rescheduling and cancellation to separate decisions.
Worked example: cancellation without a button that only pretends
Now the team adds another action to the same product. The business owner responsible for the decision has ruled that a customer may cancel their own confirmed booking themselves if it starts more than a day from now. Refunds are not part of this rule: in the example the booking is unpaid. The rule for paid bookings must not be carried over here by assumption.
The promise
The customer views their own upcoming booking, sees the cancellation condition and confirms the action. The system records the state transition, shows the result and never cancels another person's booking. Once the booking has started, cancellation through this channel is refused. A repeated request does not create several different states.
Before implementation begins, the team must define the cut-off time, the time zone, the permitted states and how repeated requests behave. If the booking is exactly 24 hours away, the rule must say unambiguously what happens. If a cancellation arrives at the same time through the admin channel, or the booking has already changed, the server checks the current state at the moment of the change, not only when the page was opened.
Checking the owner, the status and the time limit, and changing the state, must happen atomically: if another request has already changed the booking, this transition is refused, or it returns the actual state without claiming that the current request cancelled it.
Checkable examples
| Situation | Result |
|---|---|
| The owner cancels a confirmed booking 30 hours ahead | Status changes to "cancelled"; the user sees the result |
| Exactly 24 hours remain before the booking | Refuse: the rule allows cancellation only when more than a day remains |
| The owner cancels a booking 10 hours ahead | Refuse according to the rule; status unchanged |
| Another user tries to cancel | Do not change the booking; the response does not reveal its private details |
| The request is repeated after a successful cancellation | Return the defined final state, or a safe refusal, without a second side effect |
| The booking has already ended or was cancelled earlier | Do not arbitrarily overwrite the previous state |
| At the same moment an administrator changed the booking | Show the actual saved state; do not claim the customer changed it |
The stored status is only part of the result. The interface must explain the consequence of the action, and the logic checks must prove ownership, a permitted transition and the saving of the final state. Always show the actual saved state. A notification can be postponed only if the business rule does not require it; if business policy requires a cancellation confirmation, the promise is incomplete without it. "Simplifying the MVP" does not cancel an approved requirement.
What "cutting scope well" means here
The first version can be limited to one status and one channel; it can leave out admin management, exclude paid bookings until refunds are decided, and skip sending several kinds of notification if the user contract does not promise them. Such narrowing does not remove requirements agreed with the business and must show the actual saved state. What you cannot do is skip the server-side ownership check, or keep a button that always shows "cancelled" regardless of what the storage replied.
If the process owner has not yet defined the time limit or the payment consequences, a complete result for the current stage may be a documented version of the rule, a time-boxed technical spike into the integration, or a limited view. It must not pass itself off as finished cancellation. An unresolved assumption is better visible in a decision than hidden in code.
Technical prerequisites and the endless foundation
Sometimes a technical step is needed before the first finished capability. For example, the booking service has no reliable way to identify the owner, the database does not store the time zone, or a migration adding a mandatory field would break the old version of the application. Such a step may be necessary. But you should name the question it answers, the boundary it needs, and how you will establish that the prerequisite really is met.
Compare:
- "Rebuild the access system" — a change with no visible completion criterion.
- "Establish and demonstrate that the server can safely obtain the current customer and that the query against the table returns only that customer's upcoming booking; if it cannot, identify the blocker and the decision" — a bounded result.
A technical prerequisite can have a test, a schema analysis, a prototype of a compatible migration, a measurement, an environment configuration or a documented conclusion. It does not have to be a releasable feature. Its evidence answers its own question and must not simulate user-facing completion.
For data migrations, account for the versions that overlap during deployment. The common pattern — add the new field, backfill it gradually, then retire the old field — may be appropriate, but not always. First find out the data volume, the rollback rules, how the old code behaves and whether there is duplication. Adding infrastructure "for the future" to every small feature is not evidence of caution when there is no concrete threat or benefit.
A separate technical spike also does not guarantee that the next task will be risk-free. It reduces uncertainty only to the extent that its observations apply to the real system. A check against mocks, for example, does not confirm how the real payment provider behaves. Document the conditions under which the conclusion holds.
Contracts, compatibility and integration order
Two slices can look independent and still change a shared contract. Check not only the list of files but what one part promises another:
- the names and meaning of fields;
- identifiers and their types;
- statuses and permitted transitions;
- request and message formats, and the error format;
- who is responsible for the access check;
- the API (application programming interface) version, the event message or the database schema;
- requirements for the order of migrations and releases.
A hypothetical conflict between two changes
Agent A adds a server route for cancellation. It returns an object in JSON (JavaScript Object Notation, a text format for structured data) with a status field set to cancelled. Agent B adds a screen that expects a state field set to canceled. Each branch has its own test: the server test checks the first response shape, while the interface test uses a stub with the second shape. In each branch the check is green, because it reproduces only that branch's own assumption. After integration, the client cannot read the field it needs.
This is an illustration of a hidden dependency, not a real event. Tests of isolated parts do not prove that a contract is consistent. Before parallel work, the team fixes one response schema and adds a check of the real boundary, or a shared contract test between consumer and provider. After integration, it tests exactly the path that connects the API to the interface.
There is also a distinction here between independence of implementation and independence of deployment. Two developers can write code in parallel if they have agreed on the contract format. But their changes may still need a joint release if the old server does not understand the new field or the new client does not know an earlier status. Conversely, one branch can contain several components while the new feature stays switched off until compatibility is ready.
Record the contract and dependencies, name an owner for integration, and test the connected scenario against a real part of the system. If old and new versions can coexist, check their compatibility; readiness of code for deployment and availability to users have separate criteria.
A practical order:
- Fix the external behaviour and the contract the parts attach to.
- Name the technical dependency, who owns resolving it, and how it will be checked.
- Find out whether a compatible reader, writer, schema or flag must be deployed first.
- Make one integrated change and test it against another real part of the system, not only against a stub.
- Check old/new compatibility in the states where versions can coexist.
- Deploy, and open the feature to users, under separate, agreed criteria.
This is not a universal sequence of components. It is a reminder of the questions you cannot guess from file names. If the contracts do not affect each other, parallel work is fine; if they do, the work can be prepared in parallel, but integration is not declared done until the shared behaviour has been checked.
Acceptance criteria are evidence of behaviour
Every slice needs an expected result, the important refusals and sufficient evidence. An agent's confidence, the look of a page or isolated green tests do not prove that data, access and a shared contract work in the application. Before implementation, agree on:
- the starting conditions, the actor and the scenario boundary;
- the action and the data it reads or changes;
- the positive end state and the forbidden or invalid case;
- what the user sees when an expected error occurs;
- how to check that state was saved, or was left unchanged;
- external dependencies, and how to tell their unavailability apart from empty data or an access refusal;
- what is not part of the delivery.
| Kind of evidence | What it confirms | What it does not confirm on its own |
|---|---|---|
| Interface review | Text, layout and the interaction path | Access rights or persistence |
| Unit test | A local rule under given conditions | Compatibility of the whole scenario, or production data |
| Integration check | The interaction of several parts and their shared contract | Correctness of boundary rules that were not exercised |
| Permissions test | The defined policy in the chosen cases | The absence of every possible leak |
| Deployment to a limited environment | That the configuration and the chosen path work | Actual use, or benefit to all customers |
| User feedback or observation | The reaction to the behaviour that is available | Why a metric changed, without separate analysis |
Choose evidence according to risk: a text hint can be checked by review and navigation, whereas cancellation needs evidence about access, a permitted transition, a saved result and refusal. For a payment retry, check idempotency and an ambiguous network response separately; a universal checklist does not replace judgement about consequences.
For example, if the booking data provider times out, the customer should see a temporary loading error, not a false "no upcoming bookings". The read changes no state, and the system does not show success. An integration check with a deliberate timeout confirms that the response differs from an empty result, that nothing was written to storage, and that the diagnostics contain the request identifier, the dependency and the time, without personal data.
Write preconditions into the criteria so the next reader or agent can reproduce them. "Cancel your own upcoming booking" does not yet define the time limit, the status or retries. If these are unknown, mark them as a decision to make or a bounded spike, rather than forcing the implementer to guess.
Ready to enable is not always ready for users
A finished slice can be merged, deployed, enabled for part of the audience or fully available. These are different states. A feature flag can separate the presence of code from the availability of a behaviour. For example, the route already exists in the system but is shown only to internal accounts until the logs and the migration have been checked. This is useful only with a properly defined boundary, an owner and a plan for removing the flag.
A flag does not fix an incompatible schema. If deploying the code runs a destructive migration or changes a mandatory field for old clients, a hidden button will not protect the database from failures. Check separately:
- whether the deployment itself is safe;
- whether the old and new versions can run side by side;
- what the flag controls: the server, the interface, a specific role or an environment;
- whether data leaks through the API even when the screen is hidden;
- what switching the flag off will do once data in the new format exists;
- when the flag will be removed or made permanent.
"Done" should name its threshold. "Verified in the test environment", "ready to enable for staff", "passed a limited release" and "ready for all customers" are clearer than one unexplained "done". Do not call the first state the last one if an owner or a check is still ahead.
Small batches, integration and feedback
DORA (DevOps Research and Assessment, a Google Cloud research programme on software development, delivery and operations) in its 2023 report emphasises users' needs and balancing software delivery with operational outcomes. Its later guidance describes small batches as a way to shorten feedback delay and recommends making units of work independent, valuable, small and testable. This is an advisory model, not a promise that every task will fit into a day or that small size eliminates defects.
The benefit shrinks if the team bundles the batches back into one big package before testing or release: feedback arrives late, and responding to problems gets harder. First define the controls a given class of change requires, then choose a small unit that can be checked. A small diff is easier to review, but it does not prove correctness: you still need risk-based checks, as described under acceptance criteria, and frequent release is not a goal in itself. The report and the guidance do not guarantee the same benefit for every change.
A short list of changes helps a person see the change and its context. It does not prove that:
- no important invariant has been broken;
- the checks cover both the wrong and the positive case;
- two independent branches agreed on a format;
- the migration is compatible with the deployed version;
- the implementation delivers the intended product value;
- a wrong result will be noticed after release.
Each of these conditions is checked with matching evidence. DORA links small batches to other practices, such as continuous integration and feedback; you cannot attribute all the causality in its research to a single measure.
A real example of a small end-to-end fix
In FastAPI, pull request (PR) #14306 "Improve tracebacks by adding endpoint metadata" added route context to data-validation error messages. The change touched how the error is built, how the endpoint context is passed along, and checks for HTTP (Hypertext Transfer Protocol), WebSocket and mounted sub-applications. The diff of this PR showed 289 added and 16 removed lines across three files; the author added separate regression examples for requests, responses, WebSocket and sub-applications. The pull request was merged on 6 December 2025.
This is not an example of a feature with a new user-facing button. Its bounded result is diagnostic error context with the route and function names, without changing the standard response to the client. The regression tests covered metadata computation, exceptions, logging, requests, responses, WebSocket, sub-applications and cases without full context. The behaviour boundary therefore ran through several related flows rather than a single technical layer.
The PR also has limits as a source. Its own benchmarks, included in the description, are not independently verified measurements; the change does not prove that every small PR is easy to review; and merging does not rule out later fixes. The useful lesson is narrower: a team can make one locally bounded behaviour useful in several related contexts at once and list the contexts it covers with regression tests.
When you read a PR as a teaching example, keep four questions apart: what was promised, which parts were changed, which cases were checked, and what actually happened to the pull request. The title, the line count and a green status from automated checks do not replace reading the description and the diff.
Agent work: the plan splits, integration proves
A coding agent can propose boundaries, contracts, checks and an order of work, or prepare an interface and a server operation in parallel. But separate correct plans do not guarantee a compatible result: that depends on a shared contract, real integration evidence and the deployment order. OpenAI's Multi-agent documentation recommends parallelising independent tasks with a clear result and leaving short dependent steps to a coordinator; this is guidance for a particular tool, not proof of universal speed-up.
Before you launch several workers:
- Fix one expected response or data structure for all parts.
- Mark the files, schemas, types, routes and shared resources they may touch.
- Give each worker a checkable boundary and exclusions; do not split work into "everything server-side" and "the whole interface" without a compatibility plan.
- Name an owner who resolves conflicts and assembles the shared scenario.
- After execution, reconcile the actual changes against the contract, not just against the agent's report.
- Test the connected scenario and the important negative access conditions; only then decide whether the changes can be merged, deployed or opened to users.
The current Codex CLI (command-line interface) documentation describes exploring a repository, planning a change, editing and running local checks. OpenAI's official Codex Plan mode material describes proposing an implementation path before changing code; the Claude Code CLI reference documents the --permission-mode plan option. Availability depends on the product, the surface, the version and the account. Plan mode does not prove that the plan is correct or that branches are compatible. If integration could not be checked because a test environment, data or access was missing, name that limitation: "the agent said the code is ready" does not replace a check and gives no grounds to declare the slice finished.
When a different unit of work fits better
Not every goal creates new user-facing behaviour; internal work also has its own bounded result and matching evidence:
- Research. The result is an answer to a narrow question, the evidence, what remains unknown and the next decision.
- Infrastructure repair. The result is a restored process or a specific service characteristic, with before-and-after evidence.
- Security fix. The result is a closed attack path, no leakage and a regression check; the visible feature may not change at all.
- Migration. The result is a compatible data state and a supported version order, not just a new column.
- Retiring an obsolete feature. The result may be switching it off and cleaning up, with respect for dependencies, user data and a communication plan.
- System observability. The result is a signal that helps the on-call engineer distinguish a specific failure — not more logs for their own sake.
For any of these kinds of work you still need a bounded result, completion conditions and matching evidence. Not every change must deliver new product value on its own, but each must answer the question the team was asked to resolve.
Summary
A vertical slice completes a small, meaningful behaviour with the access rules and evidence it needs, passing through the necessary parts of the system without an artificial split by layer. Its boundary starts from a promise to the consumer, the rules and the criteria; narrowing the scenarios must preserve correctness, access rights and the required quality.
Finishing the parts does not guarantee release independence: you need shared contracts, a check for hidden dependencies, a migration order and integration evidence. Small batches shorten the path to feedback but do not guarantee correctness; an agent can help with the plan, but the team has to verify the connected result.
Sources and further reading
The main materials were checked on 23 September 2026; the DORA guidance on small batches was re-checked on 26 September. Each entry notes which claim the source supports. The booking and agent-branch examples are for teaching; they are not reports of real incidents.
- Ryan Singer, Shape Up: Stop Running in Circles and Ship Work that Matters, the original Basecamp web book; accessed 23 September 2026. Chapters read: "Get One Piece Done" and "Map the Scopes". The first describes early integration of a single scenario and Basecamp's experience with the visibility toggle; the second describes discovering finishable scopes from the structure of the work rather than from roles, and the author's use of ~ to mark desirable but optional additions. The Basecamp example is attributed to the author and is not presented as a universal law.
- DORA, Working in small batches, page last updated 8 December 2025; accessed 26 September 2026. Describes shorter feedback cycles and independent, valuable, testable small units, and warns against re-bundling batches before testing. DORA's recommendations are not turned here into a requirement that tasks have the same duration in every team.
- FastAPI contributors, PR #14306: Improve tracebacks by adding endpoint metadata, merged 6 December 2025; accessed 23 September 2026. The diff shows three files, 289 added and 16 removed lines, including regression tests for requests, responses, WebSocket and sub-applications. The PR supports the specific implementation and the list of tests, not the independent accuracy of the author's own benchmarks.
- OpenAI, Codex Plan mode, official material on planning mode in Codex; accessed 10 October 2026. Describes proposing an implementation path in advance and notes that availability depends on the product environment and the account; it is not evidence that plans are accurate.
- OpenAI, Run long horizon tasks with Codex, official OpenAI Developers material; accessed 10 October 2026. Gives an example of a plan with goals, constraints, milestones and checks; it describes the author's experience, not an independent measure of productivity.
- Anthropic, Claude Code CLI reference, current official documentation; accessed 10 October 2026. Describes the
--permission-mode planoption; this is a Claude Code capability, not a guarantee that a plan is correct or a permission to act. - OpenAI, Multi-agent, current OpenAI Agents API documentation; accessed 10 October 2026. Recommends giving parallel agents independent, bounded tasks and leaving dependent steps to a coordinator; this is a product recommendation, not independent proof of speed-up.