Blog

  • Story Points Are a Tax You Pay to Feel Certain

    Story Points Are a Tax You Pay to Feel Certain

    Every team we onboard at Velo arrives with a backlog full of numbers. Threes, fives, eights, the occasional heroic thirteen. Someone once explained that these are relative sizes, not time, and everyone nodded, and now every Tuesday afternoon a group of adults argue about whether a database migration is a five or an eight while the sprint clock ticks.

    We think story-point estimation is broken. Not “needs tuning” broken — structurally broken. It survives because it feels like rigor, and rigor is comforting when you’re being asked how long something will take. But the ceremony produces almost none of the value it promises, and it quietly costs teams a great deal.

    The original promise, and what actually happened

    Points were introduced to solve a real problem. Hours-based estimates were wildly inaccurate, and worse, they became commitments the moment they left the room. The fix was elegant on paper: estimate relative complexity in an abstract unit, track velocity over time, and let the arithmetic convert points to dates without ever asking a developer to promise a Thursday.

    In practice, three things went wrong.

    1. Points became hours in a hoodie. Every team eventually calibrates points against calendar time. “A five is about a day and a half.” The abstraction leaks immediately, and now you have hour estimates with extra steps.
    2. Velocity became a target. Goodhart’s Law arrives on schedule. Once velocity is tracked, it’s optimized, tickets are inflated, velocity rises, and leadership assumes throughput improved. Nothing improved.
    3. The ceremony is expensive. A team of eight spending forty minutes twice a week in planning poker is burning roughly ten engineer-hours weekly on an activity whose output is a Fibonacci number.

    The most honest sentence ever uttered in a pointing session is “I don’t know, I’ve never done this before.” Points punish that answer. They should reward it.

    What estimates are actually for

    Before proposing a replacement, it’s worth asking what problem estimation solves in the first place. Teams need three different answers, and points collapse them into one:

    • Should we do this at all? A rough sense of whether a piece of work is a week or a quarter, so it can be prioritized against alternatives.
    • Is this small enough to start? A check that the work has been decomposed to the point where an engineer can begin without a discovery phase disguised as coding.
    • When will it be done? A forecast, with a confidence interval, that leadership and other teams can plan around.

    The first is a shaping question. The second is a slicing question. The third is a statistics question. None of them are well served by asking six people to hold up a card.

    What we do instead

    At Velo we’ve replaced points with a three-part practice. It’s less ceremonial, more honest, and — we’ll show the numbers below — meaningfully more accurate.

    1. T-shirt sizes for shaping

    When new work enters the backlog, whoever is shaping it assigns a rough size: XS, S, M, L, XL. Five buckets, no arithmetic. XS is “an afternoon.” XL is “we need to break this up before it’s real.” Sizes aren’t summed, aren’t averaged, and don’t roll up into a velocity number. They exist only to answer the shaping question.

    2. A slicing rule for readiness

    No work item enters an active cycle unless it’s been sliced to a size a single person can start on Monday and demo by Friday. If it can’t, it’s not ready — full stop. This replaces the pointing debate with a much more useful conversation: how do we cut this?

    3. Cycle-time forecasting, anchored to real projects

    For the “when will it be done” question, we ignore estimates entirely and use historical cycle time. Every closed ticket has a measurable duration from “started” to “shipped.” Feed those durations into a Monte Carlo simulation over the remaining backlog and you get a probability distribution over completion dates.

    The onboarding move that makes this land for new teams: pick two named past projects of similar shape — the last checkout rebuild, the migration from Redis, whatever you’ve actually shipped — and anchor the forecast against their cycle-time distributions. The plan inherits your team’s real pace, not a generic prior. Once you have a few months of your own history, the anchors fade out on their own.

    Does it work?

    We compared the last six months of forecasts across a sample of teams using story points versus teams using the practice above. The delta was not subtle. But rather than a bare accuracy number, here’s the honest before-and-after on the axes teams actually care about:

    Dimension Story points + velocity T-shirts + cycle-time forecast
    Estimation unit Fibonacci points, debated per ticket Five T-shirt buckets, assigned by shaper
    Re-estimation trigger Manual, in a meeting, on scope drift Automatic — every closed ticket updates the model
    Forecast update trigger End of sprint, if anyone remembers Nightly, on the last N closed tickets
    Capacity model Rolling average of self-reported points Empirical cycle-time distribution
    Median forecast error 34% (2h20m/week/person estimating) 12% (~20 min/week/person)
    Design-partner retention of the practice Abandoned within 4 sprints on 3 of 8 teams Still in use on all 11 teams after 6 months

    The gap isn’t because Monte Carlo is magic. It’s because cycle time is a measurement and points are a guess, and measurements beat guesses every time you run the experiment. The retention row matters most, though. As one PM told us bluntly after her team switched:

    “I don’t need it to be faster than my spreadsheet. I need to trust it. Right now I don’t know what it just did to my plan.”

    That’s the real indictment of the points ceremony. Not that it’s wrong — it’s often close enough — but that nobody can point at where the number came from when it changes.

    An illustrative snippet

    If you want to try the forecasting piece yourself before adopting the rest, the math is embarrassingly simple. Here’s the core of what Velo runs under the hood, in about twelve lines of Python:

    import random
    
    def forecast(cycle_times, remaining_items, trials=10_000):
        results = []
        for _ in range(trials):
            total = sum(random.choice(cycle_times) for _ in range(remaining_items))
            results.append(total)
        results.sort()
        return {
            "p50": results[trials // 2],
            "p85": results[int(trials * 0.85)],
            "p95": results[int(trials * 0.95)],
        }
    

    Feed it the last hundred closed tickets’ cycle times — or, if you’re just starting out, the durations from those two anchor projects — and you’ll have a better delivery forecast in one second than most teams produce in a quarter of planning meetings.

    What you lose, and why it’s fine

    A whiteboard covered in sticky notes with sizes scrawled on them, most of them medium.
    The universal end-state of every pointing session, drawn from life.

    You lose velocity charts. You lose the ceremony. You lose the ability to say “the team delivered forty-two points this sprint” in a board deck. In exchange, you get forecasts that are three times more accurate, roughly two hours of engineering time back per person per week, and — this is the part we didn’t expect — noticeably less argument in planning meetings. When the question shifts from how big is this to how do we slice this so it fits, the conversation gets concrete fast.

    Story points were a clever solution to a real problem in 2005. We think the problem has since been solved better by simply measuring what teams do and forecasting from the measurement. If you’re still counting Fibonacci cards on Tuesdays, we’d gently suggest it’s time to stop.

  • Why our retros stopped finding the real problem

    Why our retros stopped finding the real problem

    The Friday retro ritual

    Every team we have worked on runs the same shape of retro. Sixty minutes on a Friday, three columns in a Miro board, sticky notes for what went well, what went poorly, and what to try next. Someone dot-votes. Someone else copies the top three items into a Linear ticket that nobody opens again. We used to run it that way too.

    In Q2 of last year, we shipped a release that took down billing for four hours. The retro landed the following Friday. The dot-vote surfaced “unclear on-call handoff” as the top item. We wrote a Linear ticket to rewrite the on-call runbook. Six weeks later, we shipped a different release that broke webhook delivery for two hours. The retro found the same category of problem, worded slightly differently, and produced another ticket that also went nowhere.

    The on-call runbook was fine. The problem sat upstream of it, and nobody in the room was willing to say so on a Friday afternoon with the person who owned the decision sitting three seats away.

    Why the standard retro fails

    Retros as most teams run them optimise for social comfort, not for truth. Three failure modes we kept hitting:

    • Recency bias. The team remembers Thursday’s deploy noise, not Monday’s design decision that set the deploy up to fail.
    • Consensus bias. Dot-voting rewards items several people already agree on, which selects for symptoms over root causes. Root causes are usually held by one or two people who saw them early and stayed quiet.
    • Performance bias. A live meeting is a stage. People tell the version of the story that protects the relationship, not the version that would help the next team.

    The billing incident showed us all three. The engineer who had raised a concern about the migration plan two weeks earlier did not repeat that concern in the room. Nobody wanted to spend the last hour of the week relitigating a decision that felt settled.

    The retro found something true and small. It missed the something true and large, because the format could not hold it.

    What we do instead

    We replaced the Friday retro with three artefacts, spread across the week. None of them takes more time than the meeting they replaced. We have run this shape for eleven months across two product squads and one platform squad.

    1. A written pre-mortem, filed on Wednesday

    Whoever owned the incident, feature, or sprint outcome writes a one-page pre-mortem in Notion. It is not a report of what happened. It is a written attempt to answer one question: if this failure repeats in six months, what will the story be? The author writes it alone, without review, and posts it in the squad Slack channel by end of Wednesday. It is time-boxed to forty-five minutes. Long documents mean somebody is hiding.

    2. A two-question survey, sent Thursday morning

    Every person on the squad, plus two adjacent stakeholders (usually a designer and a customer support lead), gets a Google Form with two questions:

    1. What did you see, hear, or think during this work that you did not say out loud?
    2. If you had a private ten-minute conversation with the person most responsible for the outcome, what would you ask?

    Answers are anonymised by the facilitator and pasted into the Notion doc under the pre-mortem. Response rate sits above ninety percent because the questions are specific and the form takes under five minutes.

    3. One blameless conversation, Monday at 10am

    The squad meets for thirty minutes on Monday. The pre-mortem and the survey answers are already in the room. The facilitator, who is not the tech lead, reads three or four survey answers aloud and asks the author of the pre-mortem to respond to them. No sticky notes. No dot-voting. No action items produced in the meeting itself. Proposals get added to the Notion doc during the following twenty-four hours, once people have had time to think.

    What changed

    The billing incident was one of eight retros we ran through the old format. The webhook incident was the ninth. Between month four and month eleven of the new format, we ran six retros across incidents of similar severity. Two produced Linear tickets that closed within a sprint. Three produced changes to how we scope Datadog dashboards before we ship, not after. One produced a decision to stop building a feature that two engineers privately thought would not land, and had not raised in a Friday meeting.

    The change is not that we find more problems. It is that the problems we find are the ones that matter. Three shifts explain most of it:

    • Writing before speaking gives people room to admit things they would not admit in a room.
    • Splitting the process across three days lets recency bias fade.
    • Removing the ritual of “action items produced in the meeting” removes the pressure to produce something visible, which is what pushes teams toward the easy, wrong answer.

    What we still get wrong

    The Monday conversation is fragile. If the facilitator lets it become a debate about the pre-mortem’s conclusions, it collapses back into the old format. We have had two of those in the last year. Both times, the survey answers that mattered most did not get read aloud, and the meeting ended with everyone agreeing on a symptom.

    We have also not solved the problem of what to do when the person most responsible for the outcome is the tech lead running the process. We rotate facilitation to a peer squad’s engineer in those cases, but it is not a clean answer.

    The retro, as most teams run it, is a meeting that produces the feeling of learning without the substance of it. If your Linear board carries three open “improve on-call handoff” tickets from three different retros, that is the signal.

  • The five-day slice rule

    The five-day slice rule

    Every Monday at 10:15 our delivery lead opens Linear and runs a filter called Cycle Ready. If a ticket in the active cycle fails the filter, it gets flagged red and the ticket owner has until standup Tuesday to fix it or pull it. The filter checks one thing: can a single engineer start work Monday morning and demo something running by Friday afternoon.

    We call this the five-day slice rule. It has been the single biggest change to how our team ships since we moved off two-week sprints eighteen months ago.

    Why five days, not ten

    We tried ten-day cycles for most of 2024. Tickets came in sized small, medium, or large, and engineers estimated them in points. By day seven of a typical cycle, roughly a third of medium tickets were still labelled In Progress with no visible artifact. Reviewers had nothing to look at. QA had nothing to queue. The last three days always became a scramble.

    The pattern was consistent enough that we started tracking it in Datadog against our Linear webhook data. Tickets that produced no reviewable artifact by day three of a ten-day cycle had a 72 percent chance of slipping the cycle. Tickets that did produce something by day three slipped 8 percent of the time. The signal was loud.

    We cut the cycle in half and enforced a hard rule on entry: one person, Monday start, Friday demo. If it does not fit, split it before the cycle starts, not during.

    How we enforce the rule

    The rule lives in three places:

    • A Linear template with a required field called Friday demo artifact. Engineers cannot move a ticket into Ready for Cycle without filling it in. Sample entries: a PR merged behind a flag and hitting a staging endpoint; a Grafana panel showing p95 for the new route; a Loom of the empty state rendering with fixture data.
    • A pre-cycle review meeting on Friday afternoon called Slice Check. Twenty five minutes, four people: engineering manager, tech lead, product manager, delivery lead. We read the Friday demo artifact field for every candidate ticket. If anyone at the table cannot picture the demo, the ticket does not enter.
    • A Slack bot posting into the delivery channel every Wednesday at 4pm with the list of active-cycle tickets that have no PR opened and no draft artifact linked. The message pings the ticket owner directly.

    The bot is the piece that took the longest to trust. We tuned it for six weeks before people stopped arguing with it. The current heuristic: no draft PR, no Loom link, no Notion doc updated in the last 48 hours, and the ticket is past cycle midpoint. Three signals, one ping.

    What happens when a team pushes back

    The rule gets fought. Usually by whichever team is holding the largest piece of unsplit work. We have heard every version of the objection:

    You cannot split a database migration into five-day slices. The migration either runs or it does not.

    The auth rewrite is one atomic change. Splitting it means shipping something broken.

    We are being asked to do more planning work than shipping work.

    We take these seriously and we still hold the line. Every migration we have run in the last year has split. Every auth change has split. The planning cost is real and it front-loads. Our data shows the front-loaded planning cost is roughly 90 minutes per split ticket, and it saves an average of 6 hours of end-of-cycle scramble per unsplit ticket that slips.

    When a team insists a piece of work cannot split, we sit down with them for a 30 minute session with a whiteboard and the delivery lead. We have run this session 41 times. It has produced a valid split 39 times. The two exceptions were a vendor cutover with an external deadline and a hotfix that shipped inside a day.

    A concrete example: splitting the profile export ticket

    Last quarter we had a ticket that read: add data export for user profiles, including preferences, integrations, activity history, and audit logs, downloadable as a signed ZIP. Original estimate: two weeks. Owner: one engineer on the Growth pod.

    Under the old rules this would have entered a cycle whole. Under the slice rule we ran it through Slice Check on the Friday before, and split it into three tickets:

    1. Slice one, week of Jan 13. Endpoint scaffold plus preferences payload. Friday demo: hit POST /exports/profile in staging, receive a signed URL, download a ZIP containing a single preferences.json file. Behind a flag. One engineer, five days.
    2. Slice two, week of Jan 20. Add integrations and activity history to the payload. Friday demo: same endpoint, same flag, ZIP now contains three files. Handles the 90 percent case of activity records fitting in a single query batch.
    3. Slice three, week of Feb 3. Audit logs, pagination for large history sets, signed URL expiry policy, and flag flip. Friday demo: end to end run for a real customer account with 40k audit rows, timing recorded in the demo doc.

    We put a two-week gap between slice two and slice three on purpose. That gap ran a quiet beta with three friendly customers on slice two, and the feedback moved the audit log format before we built it.

    Total calendar time: roughly the same as the original two-week estimate would have been had it not slipped. The difference is that we had something demoable at three checkpoints instead of one hopeful checkpoint at the end.

    What we track

    We keep three numbers on a Notion page called Delivery Health:

    • Percent of active-cycle tickets that hit their Friday demo artifact. Current: 88 percent, target 85.
    • Median time from a ticket entering Ready for Cycle to its first PR opened. Current: 1.2 days.
    • Number of tickets that entered a cycle unsplit and slipped. Current: 2 this quarter, down from 14 the same quarter last year.

    The rule is not magic. It is a constraint that forces the planning conversation to happen on Friday instead of Wednesday of week two, when a slip is already priced in. If the demo cannot be pictured on Friday, the work is not ready. That is the whole rule.

  • The quarter we shipped no features

    The quarter we shipped no features

    Last October, three days after our Q4 planning offsite, we made a decision that felt reckless at the time. We were going to spend the entire quarter without shipping a single new feature. No new modules, no new integrations, no new dashboards. Only bugs, docs, and the internal tools our engineers had been asking for since spring.

    Our head of sales, Mira, found out on a Monday morning during our weekly go-to-market sync. She went quiet for about eight seconds, then asked whether we were serious. We were.

    What sales was worried about

    Mira had four deals in the pipeline that hinged on a specific promise: a Snowflake connector we had been talking about since June. Two of those deals were mid-market, one was a renewal expansion, and one was a competitive replacement worth around 180k in annual contract value. She pulled up the deal notes in Notion and walked us through each one.

    The fear was reasonable. If we froze features for 90 days, three things could happen:

    • Prospects would walk to competitors who kept shipping.
    • Existing customers waiting on requested features would churn at renewal.
    • The sales team would lose narrative ammunition on discovery calls.

    We agreed to review the freeze monthly. If any of those signals showed up in the data, we would call it off. Mira asked us to write down what “showed up in the data” meant, so we did: net revenue retention below 108%, gross churn above 1.4% monthly, or two consecutive weeks of stalled pipeline movement on flagged deals.

    What we shipped instead

    The engineering team split into three squads. One squad, which we called Fixit, worked exclusively through Linear tickets tagged with the “customer-reported” label. Another squad, Docs, sat with our support lead every Tuesday to identify the top ten most-hit help center pages and rewrite them. The third squad, Tooling, built the internal admin console engineers had been begging for.

    By week six we had closed 247 bugs, some of which had been open for over a year. The Datadog dashboard we cared about, the one tracking p95 API latency, dropped from 840ms to 310ms after two engineers rewrote a query planner in the reporting service. Our support team went from 34 open Zendesk tickets on any given Friday to 9.

    The internal admin console was the surprise. Before Q4, resolving a customer-reported billing issue took an engineer about 40 minutes: pull data from three tables, reconcile in a Google Sheet, patch, verify. After the tooling squad shipped the console, our support engineers were doing the same work in under 4 minutes. They did not need to page anyone.

    By the end of week ten, our on-call rotation had gone from one incident per shift to one incident every six shifts. Two engineers told me they were sleeping better. One of them had been talking about leaving.

    What happened to churn

    Here is the part nobody predicted. Gross churn went down. Not by a huge amount, but measurably: from 1.2% monthly at the start of Q4 to 0.7% by December. Net revenue retention held at 114%.

    Mira’s Snowflake deals: three of the four closed anyway. The connector question came up on discovery calls, and the answer we gave, which was that we were spending the quarter on reliability instead of new surface area, played better than we expected. One of the buyers, a VP of data at a healthcare company, told us he had never heard a vendor say that out loud. He signed in November.

    Two effects we did not model

    First, our NPS moved from 42 to 51. We got unsolicited notes in Slack from customer success managers whose accounts had stopped filing tickets. Second, our engineering hiring pipeline got healthier. Three candidates in December mentioned during their onsite loop that they had read our internal writeup about the freeze and wanted to work at a place that took reliability seriously.

    What we would do differently

    We got lucky on a few things and would not repeat every choice.

    1. We underestimated how disorienting the freeze would feel to product managers. Two of them felt sidelined for six weeks before we figured out how to give them meaningful work reviewing customer feedback and shaping the Q1 roadmap.
    2. We should have communicated the freeze to customers on day one, not week three. When we finally sent the note explaining what we were doing, the response was overwhelmingly positive. We could have banked that goodwill earlier.
    3. We did not set clear exit criteria beyond the churn and NRR thresholds. When Q1 planning arrived, some of us wanted to extend the freeze another month, and we did not have a decision framework for that conversation.

    We are not going to do this every quarter. Growth still matters, and a company that only fixes bugs is a company that gets displaced. But we now know the shape of what a deliberate pause looks like, what it costs, and what it returns. Next time we consider one, the conversation will be shorter, and the fear in the room will be smaller.

  • When to open the Board, the WBS, or the Timeline

    When to open the Board, the WBS, or the Timeline

    Every project in Velo shows up in three shapes. The Board is where a card moves from Doing to Done. The WBS (work breakdown structure) is where the project gets decomposed into deliverables and sub-deliverables before anyone touches a ticket. The Timeline is what we show the CFO on Thursday when she asks whether the migration will land before Q3 close.

    We built all three because a project has three audiences: the person doing the work, the person shaping the work, and the person funding the work. They need different resolutions of the same truth.

    The Board is for the next 72 hours

    Open the Board when you want to know what is moving today. It answers: which cards are in progress, which are blocked, who owns them, and what will ship by Friday. Columns default to Backlog, Ready, Doing, Review, Done. We use the same keyboard shortcuts as Linear so nobody on the team has to retrain their fingers when they switch tools.

    Rules of thumb for when the Board is the right lens:

    • Daily standup at 9:15
    • Triaging a Slack ping from support about a production bug
    • Deciding whether to pull in a stretch card during the sprint
    • Checking WIP limits before merging another design review

    The Board hides context on purpose. You will not see when the epic started, how it maps to Q3 goals, or whether the effort estimate has drifted from the original budget. That absence is the feature. During execution, more context slows you down.

    The WBS is for the first two weeks and the ugly middle

    The WBS is where a project is born. Before we open a Board we sit with the product lead and break the initiative into deliverables, then those into sub-deliverables, then those into work packages. A migration project we ran last quarter had 4 top-level deliverables, 19 sub-deliverables, and 63 work packages by the time we stopped decomposing. Each work package eventually became one or two cards on the Board.

    The biggest mistake we see, in our own team and in every team we onboard, is this: people build a WBS in week one, move everything to the Board, and then never open the WBS again for the rest of the project.

    That is where projects die quietly. The Board tells you what is in flight. It does not tell you what you promised, what you dropped, what got scoped out, or what nobody has picked up because it sat in a sub-deliverable that never got carded. Six weeks into a project, the Board looks healthy and the actual deliverable is missing a third of its scope.

    We now run a WBS review every second Wednesday. Fifteen minutes, one question per branch of the tree: is this deliverable still on the plan, or did it fall off the Board without a decision? About one time in five we find a work package that should be a card and is not. About one time in ten we find a card doing work that is not in the WBS at all, which is a scope conversation waiting to happen.

    The Timeline is for people who do not touch the work

    The Timeline is a rolled-up Gantt with milestones, phase bands, and dependencies. It is what we send to the exec sponsor, the finance partner, and the customer stakeholder on a Tuesday-morning update email. We do not use it to plan work; we use it to communicate about work.

    A few things the Timeline does well:

    1. Shows the critical path when a dependency slips.
    2. Shows milestones against calendar dates, not sprint numbers.
    3. Shows phase overlap, so a stakeholder can see that Discovery and Design were meant to overlap by two weeks.
    4. Exports cleanly into the steering committee deck.

    If your PM lives in the Timeline, something is off. The Timeline is a read-only surface for people outside the working team. When we catch ourselves editing the Timeline directly, it is a signal that the WBS underneath is stale.

    A working rhythm

    Here is the cadence we default to on a new project, and the one we recommend when a team onboards to Velo:

    • Week 1: WBS only. No Board. No Timeline. Decompose until every leaf is estimable in a day or two.
    • Week 2: Cards get generated from work packages. Board opens. First sprint starts.
    • Every sprint: Board is the daily surface. WBS gets a 15-minute review mid-sprint. Timeline gets refreshed once, on the day before the stakeholder update.
    • Project close: We close the loop in the WBS, not the Board. A card marked Done is a promise kept only if the parent work package was in the original plan.

    Three views, one project, three different questions. The Board asks what is moving this week. The WBS asks what we promised at the start. The Timeline asks what the outside world should expect. Skip any of them and the project drifts in a way that is hard to see until it is expensive to fix.

  • How we unbreak a Wednesday sprint without cancelling it

    How we unbreak a Wednesday sprint without cancelling it

    Every team we know has had that sprint. Monday standup looked fine. Tuesday morning a payment webhook started dropping in staging, one engineer went out sick, and the design review pushed the checkout redesign back by two days. By Wednesday afternoon the burndown on our Linear board was flat, six tickets deep, and the sprint goal read like fiction.

    We used to cancel sprints in this situation. We stopped doing that around a year ago. Cancelling costs us the retrospective, the sense of finishing something, and the muscle memory of shipping on a cadence. What replaced it is a Wednesday recovery ritual that we run in about forty minutes.

    The Wednesday triage, not another standup

    We block thirty minutes on Wednesday at 2pm called “Sprint check”. It only fires when the burndown deviates more than twenty percent from the ideal line, which our Datadog dashboard flags in a Slack channel called #eng-signals. If the sprint is on track, the meeting is cancelled by 1:45pm and nobody joins.

    When it does fire, three people attend: the engineering lead for the squad, the product manager, and whoever picked up the on-call pager that week. No designers, no wider group. The point is a fast, honest read on the remaining ten working hours across four engineers.

    The question we ask is not “can we still finish everything?” It is “what one thing, if shipped by Friday, would make this sprint worth having run?”

    That one question forces a decision that the daily standup rarely produces. Standups report status. Wednesday triage rewrites the plan.

    Cut scope in the second half

    Once the anchor ticket is named, we walk the remaining Linear tickets and sort them into three buckets. We do this on a shared Notion page titled “Sprint 47 midweek reset” with three headings and drag ticket links under each.

    1. Ship this week. The anchor ticket and anything on its critical path. Usually two or three tickets.
    2. Defer to next sprint. Work that is not blocking anyone. Move it back to the backlog with a comment explaining why.
    3. Drop, do not defer. Tickets that felt urgent on Monday and no longer do. These get closed with a short note. If they matter again, someone will reopen them.

    The third bucket is the one that saves us. Roughly a quarter of what we plan on Monday gets dropped rather than deferred, and none of it has come back to bite us in the four sprints we have tracked this pattern.

    Slice the anchor, do not shrink it

    The highest value ticket is where teams tend to lie to themselves. On Wednesday we do not promise a smaller version of the same scope. We split the ticket into two Linear issues, and the parent becomes an epic.

    Take our checkout redesign example. The original ticket read: “Ship the redesigned checkout flow with saved cards, address autofill, and Apple Pay.” By Wednesday it was clear we had ten hours of engineering work left and about twenty hours of scope. The split looked like this:

    • PAY-412: Ship the redesigned checkout behind a feature flag, five percent rollout, saved cards only. Owner: Priya. Estimate: eight hours.
    • PAY-413: Address autofill and Apple Pay under the same flag. Owner: unassigned. Moved to next sprint.

    The slice we ship on Friday is a real, running thing in production, even if it sits behind a flag at five percent. Next sprint we widen it. What we avoid is the trap of promising the whole checkout by Friday, then delivering nothing and calling it a spike.

    The rolling change log

    Every scope change goes into a single Notion page we call the Sprint Ledger. One page per sprint, appended to as things move. Each entry has a timestamp, the ticket ID, what changed, and one line of why.

    Wed 14:32  PAY-401  Dropped. Duplicated by PAY-397 already in progress.
    Wed 14:35  PAY-412  Split from PAY-388. Anchor for the week.
    Wed 14:41  ONB-215  Deferred. Blocked on design; no unblock this week.

    The ledger takes about six minutes to fill in during triage. It is read twice: once by the wider squad on Wednesday afternoon, and once by the retro facilitator on the following Monday. Nobody hunts through Slack scrollback trying to remember what changed and why.

    What we get back

    Four things, measured over ten sprints since we started running this ritual:

    • We now finish about eighty percent of our stated sprint goal by Friday, up from around fifty percent when we would grind on the original plan or cancel outright.
    • Retros focus on cause, not blame. The ledger tells us what happened; we can talk about why.
    • Product managers push back less on Wednesday cuts, because the anchor is preserved and the ledger makes the trade visible.
    • On-call load in the second half of the sprint dropped, because we stopped shipping half finished work under time pressure.

    None of this requires new tooling. Linear, Notion, Slack, one recurring calendar block, and a rule about when it fires. The hardest part is not the process; it is the willingness on Wednesday afternoon to say out loud that Monday’s plan is no longer the plan, and to write down what replaced it before the day ends.

  • What happened when we replaced standup with a doc

    What happened when we replaced standup with a doc

    The reason we cancelled the 9:15

    Our morning standup ran for eleven months before we killed it. Fifteen minutes on paper, twenty-two in practice, five people on video, one person still chewing toast. We tracked the cost for a quiet month: eleven engineers, four days a week, roughly nine hours of collective time each week vaporized into “yesterday I worked on the invoice bug, today I will keep working on the invoice bug.”

    The trigger was Linear. We had migrated our ticket board over from Jira, and the state of every ticket was suddenly legible without a human reading it aloud. Whatever the standup had been for, it was no longer for status.

    We wrote a shared Notion page called Daily. Each morning, before 10:00, you posted three lines: what shipped, what you are on, what is blocking you. That was the whole ritual. No meeting.

    Week one and two: something went wrong

    The first Monday felt like a small miracle. Everyone got the extra half hour, the doc filled up by 09:47, and we all stayed at our desks. By Thursday, two things were off.

    The doc was thinner than we expected. People wrote “same as yesterday” or copied their previous line and edited the date. The prose that had felt lively when spoken went dead when typed. Morale dipped too, in a way we did not predict. Not a crash, more like the room got quieter. Our head of design put it best in the retro:

    I did not realize how much of my sense of the team came from watching Priya groan about her PR review queue and Marcus talk about his kid’s soccer game. The doc has none of that. It reads like a receipt.

    Slack traffic went up during those two weeks, in a way that surprised us. Random channels filled with the small talk that had been living, uninvited, inside standup. We had removed the container without moving the contents anywhere. The contents leaked.

    The other loss was harder to measure. In standup, someone would say a sentence and someone else would say “wait, back up,” and a small course correction would happen in ninety seconds. In the doc, that same correction became a threaded Slack conversation that took two hours and ended with “let’s hop on a quick call.” We had traded one meeting for many smaller ones.

    Week three onward: the writing got serious

    Around the third week, the doc improved. A few things changed at once:

    • The team stopped treating the entry as a chore and started treating it as a note to a colleague. Entries got longer, more specific, funnier.
    • Blockers became first class. If you wrote “blocked on the Stripe webhook signature bug,” someone unrelated would read it over coffee and drop a link to the fix.
    • Async engineers in Lisbon and Toronto stopped feeling like second tier attendees. Their entries carried the same weight as the ones written in the London office.

    The deeper win was the writing itself. Engineers who had been quiet in meetings wrote sharp, thoughtful updates. One of them told us she had spent every standup rehearsing her sentence and missing what other people said. The doc gave her back that attention. Our design docs improved in the same period. We are not sure the two are causally linked, but it felt like a muscle getting more reps.

    The Wednesday sync, and why it stayed

    We tried holding out on the meeting free plan for a full month. It did not last. By week five we had put one meeting back on the calendar: a thirty minute sync on Wednesday at 10:00, camera on, no agenda beyond “how is the week going.” We call it Wednesday. That is the whole name.

    Wednesday is not a status meeting. If you tried to give a status update in it, someone would gently redirect you to the doc. It is where the soccer game goes, where the “I hate this ticket” goes, where the “I think we should reconsider the pricing page rewrite” goes. It has run for fourteen months now and we have cancelled it twice, both times for a company offsite.

    The compromise on paper:

    1. Monday through Friday: the Daily doc, posted by 10:00.
    2. Wednesday at 10:00: the thirty minute human sync.
    3. Friday afternoon: a short written retro in the same Notion space, three prompts, no meeting.

    Time saved per engineer, measured against the old standup, is roughly six hours a month. That number is real. What is less measurable, and what we think matters more, is that our written record of what happened each week is now searchable. When a new hire joins, we point them at three months of Daily entries and they get context that used to live only in people’s heads.

    What we would tell a team about to try this

    Two things. First, the social contract of standup is doing work you cannot see. If you remove the meeting without replacing the social part, the team will feel it within a week and blame the doc. Put the social part somewhere on purpose. Second, the doc will be bad for two weeks. Do not judge it before then. The writing muscle needs reps, and the first entries will read like receipts because the team is still thinking in bullet points from the meeting they no longer have.

    This setup would not work for a sales team, or a team spread across more than three time zones, or a team that responds to Datadog pages every hour. It works for eleven engineers and four designers who write for a living anyway. Your mileage will vary. Ours has not, in a year and change.

  • Velocity is a lie detector not a speedometer

    Velocity is a lie detector not a speedometer

    Every quarter we watch another engineering team roll a velocity chart into a review deck, gesture at the bars trending up and to the right, and declare progress. We used to do it too. Then we ran a small experiment on our own Velo delivery team: we hid the velocity number from three squads for a full quarter and gave them cycle time instead. Two of the three squads shipped more features. All three reported that planning felt less theatrical.

    This is not an argument against measurement. It is an argument against a specific metric that has quietly stopped telling us what we think it tells us.

    Velocity measures the ruler, not the road

    Story points are a ruler the team invented. When we grade a team on how many units of their own ruler they produce per sprint, we should not be surprised when the ruler starts stretching. We have watched this happen on our own boards. A ticket that would have been a 3 in April becomes a 5 by July. Nobody lies. Everyone remembers “that thing that turned out harder than expected,” and the estimate drifts up. The chart climbs. The output does not.

    Goodhart’s law shows up in the standup:

    When a measure becomes a target, it ceases to be a good measure. Velocity is the most polite example of this rule we have found in software.

    The other failure mode is subtler. Velocity averages hide the shape of the work. A team can hit 42 points every sprint for six sprints and be quietly falling apart, because 40 of those points come from a single engineer who is one Slack DM away from resigning. The bar chart cannot see that. Cycle time can.

    Cycle time is boring, which is the point

    Cycle time is the elapsed clock between “in progress” and “done.” It is boring because it measures reality rather than an estimate. We cannot inflate it by talking about it in a Wednesday grooming session. It refuses to care about our narrative.

    Here is the comparison we now put in front of every engineering lead we hire:

    • Unit. Velocity uses story points, a team invented currency. Cycle time uses hours or days, a currency everyone shares.
    • Gameable by. Velocity is inflated by re estimation, ticket splitting, and status theatre. Cycle time is inflated only by shipping faster, which is what we wanted.
    • Sensitive to. Velocity is sensitive to who is in the room during planning poker. Cycle time is sensitive to review queues, environment flakiness, and handoffs, the things that slow us down.
    • What it hides. Velocity hides bottlenecks behind an average. Cycle time exposes them by widening the tail of the distribution.
    • Actionable signal. A dropping velocity prompts a debate about commitment. A widening cycle time prompts a debate about the pull request that has been open since Tuesday.

    What we changed on our own board

    We run Linear for tickets and pipe every state transition into a Datadog dashboard. Once a week, on Thursday afternoon, we look at three numbers together:

    1. Median cycle time for tickets closed that week.
    2. The 90th percentile of the same distribution.
    3. The count of pull requests older than 48 hours.

    We deliberately do not look at velocity anymore. When a stakeholder in Notion asks “how much did we ship this quarter,” we answer with a count of shipped tickets and a link to the changelog. If they push, we show the cycle time trend. Nobody has pushed twice.

    The 90th percentile is the metric that changed our behaviour the most. Medians are polite. They hide the ticket that sat in code review for eleven days because the reviewer was on parental leave and nobody rerouted it. The 90th percentile has forced us to build a bot in Slack that pings the review channel every morning at 9:15 with any pull request older than a day. Our median moved a little. Our tail moved a lot.

    The objections we still hear

    Two objections show up in every conversation about this, and both deserve a real answer.

    The first is that cycle time punishes big work. A refactor that takes two weeks will show up as a fat cycle time number and drag the median. Our response: split the refactor. Not into fake tickets that ship in isolation, but into shippable, reversible steps. If a piece of work cannot be split, that is itself a finding, and the fat number is telling us something true.

    The second objection is that cycle time can also be gamed. Engineers can open tickets late, close them early, or keep everything in draft. Fair. We have seen all three. The difference is that these games are visible in the version control log and in the Linear activity feed. Velocity inflation is invisible, because the ruler itself is invisible. A gamed cycle time becomes a conversation. A gamed velocity becomes a slide.

    The metric we would keep if we could only keep one

    If a new engineering leader joined tomorrow and asked which single number to track, we would not hesitate. Track the 90th percentile of cycle time, week over week, and set a target for the tail rather than the average. That number is close enough to reality that people can argue about it usefully, and far enough from planning theatre that it does not warp under pressure. Velocity charts belong in a museum next to lines of code per day. We stopped drawing them and started drawing something harder to fake.

  • What done means for a task on our team

    What done means for a task on our team

    Every team we worked on before Velo had a definition of done pinned to a wiki page nobody read. Ours did too, until a Wednesday standup in March when Priya asked whether the invoice retry work was finished, and four engineers gave four different answers. That morning cost us a customer refund and a two hour incident review. We decided the Notion page was not the problem. The definition was.

    Three tries that did not stick

    Our first attempt was a paragraph in Notion titled “shipping standards” that said tasks should be “merged, tested, and reviewed.” It read fine on the page. In practice, “tested” meant whatever the author felt like: a unit test, a manual walkthrough, or nothing if the diff was under twenty lines. We shipped a race condition in the billing worker three weeks later because the author had run the change against a fresh database and assumed that counted.

    The second attempt was a Linear checklist template with nine items. Everyone checked every box, because the boxes were reported by the author and the reviewer had no way to verify half of them without opening five other tabs. The checklist became a ritual, then a joke, then a template we quietly stopped applying to new tickets.

    The third attempt was strict: a task was done when a designated QA engineer signed off in a Slack thread. This lasted eleven days. Our QA lead, Ruth, went on holiday, and the queue backed up to forty two tickets. When she came back, half the context was gone and she had to re verify work from memory. We had traded ambiguity for a bottleneck.

    The four criteria we settled on

    After the third failure, we spent a Friday afternoon working through what we needed the definition to do. It had to be verifiable by someone other than the author, it had to survive one person being out, and it had to answer the question Priya asked in March without a debate. We landed on four criteria, in this order:

    • Works: the change does what the ticket says, verified against the acceptance criteria written before the branch was cut. If those criteria were vague, that gets fixed before the ticket moves to review, not after.
    • Tested: automated coverage exists for the new behavior, and the tests fail without the change. The reviewer runs the suite locally or points at a green CI badge tied to the merge commit.
    • Deployed: the change is live in production, not staging, not behind a flag that has never been flipped on for a real user. If the work sits behind a flag, done waits until the flag is on for the intended audience.
    • Observed: a human has confirmed the change behaves as expected in production, using logs, a Datadog dashboard, or a real user event. Not a synthetic ping. A trace of the feature being used, or a metric moving in the direction we predicted.

    The order matters. If “works” is unclear, testing the wrong thing is worse than not testing. If we skip “deployed” and call something done at merge, we hide half our incidents in the gap between main and production.

    The compromise on observed

    Observed was the criterion that almost killed the whole definition. Half the team pointed out, correctly, that internal only changes have no production traffic to watch. A new admin report, a migration script, an internal CLI: none of these throw off metrics on the customer dashboards we use for observability. Waiting for a real user event on an internal tool would mean waiting forever, or fabricating one.

    We debated dropping the criterion for internal work. We tried, for a sprint. Two internal tools broke silently and we found out from a support agent who could not load the refunds page. The criterion needed to survive.

    The compromise: for internal only changes, observed means the author or a teammate has used the feature in production for its intended purpose, with a Loom or a screenshot posted to the ticket. Not tested it. Used it. If the ticket is a migration, the observation is the query result after the migration ran. If it is a CLI, it is the terminal output from a real invocation on the real database.

    The distinction we care about is between “I believe this works” and “this has done its job for a real person, once.” The Loom feels heavy the first time. It stops feeling heavy the second time somebody catches a broken admin page before a customer does.

    How the four criteria show up in our week

    Every ticket in Linear now has four checkboxes matching the criteria. The author checks the first three. The reviewer, or on internal changes any teammate, checks observed and pastes the evidence. Our Monday planning meeting starts by pulling the list of tickets marked done in the last week and skimming the observation links. It takes eight minutes. In the six months since we adopted this, we have had two rollback situations that a proper observation caught before the on call engineer noticed. We have also had one case where the observation link was a screenshot of the wrong environment, which is a different problem, and one we are still working on.

    We do not think this definition is universal. It is what our team of eleven engineers, on a codebase with sixteen deploys a week, needs to keep the wiki page honest. If the shape of the team changes, we expect the definition to change with it.