Roadmap decisions rather than dates.
One thing that bothered me about Imprint’s product after joining was our lack of passkey support. Passkey support is a rare opportunity to increase resiliency to phishing attacks while simultaneously reducing login friction. If it’s good for our members, our partners, and our product, it felt like something we should have already shipped. Nonetheless, it was hard to get it onto the roadmap alongside everything else we were working on.
To dig into passkeys, I started sketching out the implementation as a side quest. Some iterations later, I had something implemented behind a disabled feature flag for team review. At that point, most problems had a concrete solution implemented, and the remaining issues were messy intersections between passkey implementation and user experience. Issues remained, but the tangible implementation made tradeoffs explicit, and we were able to work through them. Soon thereafter, we launched passkeys to a small group in our web experience, iterated on feedback, finalized the details, and brought those details forward to our native mobile experiences as well. It never got onto the roadmap, but it did ship.
Our passkey release planted a seed for me, but it required another experience to fully germinate. We had a discussion about hitting a date for a product extension we’re developing. Our conversation kept anchoring on the idea that pulling in a date was dependent on pushing out dates for other projects. Presenting two conflicting projects as requiring timeline tradeoffs wouldn’t have caused me to blink an eye five years ago, but in this conversation it inspired a sort of instinctual revolt: with modern development techniques, I believe very few projects are essentially constrained by execution bandwidth. Some are constrained by approvals, others are constrained by cross-team and cross-functional handoffs, and many are constrained by missing decisions, but almost none should be constrained purely on time. Shifting blocks of time across projects to speed up delivery felt wholly divorced from the reality I work in.
Grappling with my frustration for some time didn’t lead to a profound awakening, but it did help me bring together my thoughts about how effective modern organizations operate. The modern organization operates most effectively by orienting itself around highly empowered teams who broaden their capabilities to absorb cross-team and cross-functional handoffs, can rely on automated harnesses to address most remaining cross-functional approvals, and accelerate high-quality decision making through rapid prototyping. The premise here is an old one, but AI-tooling has shifted what’s possible in remarkable ways.
Reducing handoffs with team and individual accountability
In 1975, Fred Brooks’s The Mythical Man-Month warned us that adding individuals to a late project makes that project even later rather than speeding it up. However well-understood this is, it remains extremely common for leaders to repeat this mistake, even today. My personal theory is that most leaders performing the “ask for more staffing” gambit intuitively know they are optimizing to “get stuck” in a way that shifts responsibility off them because they didn’t get the requested funding, rather than that there is no way to accomplish the task at hand.
Even if teams avoid the simplest versions of the mythical man-month’s trap, organizations that operate through strict ownership of defined boundaries introduce the same problem. Well, of course your team can’t launch that without approval from the architecture team and the security team and performing a user research study. That’s not really your team’s ownership area, you’ll need approval from the membership accounts team, at a minimum! Agile, with the idea of collective code ownership, rejected this idea in principle, although certainly it remains messy in practice.
Even with strong AI-enhanced workflows, I still find human review of load-bearing technical decisions (e.g. a decision that will meaningfully impact subsequent decisions) to be extremely valuable. This is why the highly-empowered team model is an effective compromise: there needs to be an experienced engineer whose perspective is infused into the architecture, who shares the team’s context, and who is aligned with the team’s priorities. This creates space for earlier career folks to be highly effective, while also allowing teams to largely operate independently across complex, intersecting goals.
The final piece of driving accountability is rejecting the argument that a problem is too confusing or too hard for someone to know the consequences of their work. There are a lot of hard problems, and that’s what makes creating software such an interesting job. However, in a world with infinite feedback readily available, even if that feedback is of inconsistent quality, getting surprised or being unable to unpack a problem is an algorithmic gap in how work is being approached. Learning how to manage that gap is a core part of being an engineer, and something we are accountable for learning.
To be explicit, the argument isn’t that folks should be flawless: mistakes are fine, even laudable in some cases. Laudable mistakes are almost always couched in a web of derisking, often feature flags, to make them inexpensive. If someone isn’t increasingly effective at derisking their decisions throughout their career, then they’re not being accountable for their impact as an engineer.
The above section has been fairly standard advice for decades, but I believe AI-tooling has substantially changed three dimensions of this approach:
Maintained context in e.g.
AGENTS.mdfiles can improve the overall approach of the company working on a given area. It’s very hard to train an entire company, but today you can get many of the outcomes of training by manipulating centralized context. This makes it far easier to operate decentralized teams than in prior eras.Inconsistent patterns in a codebase will “poison” an LLM’s context window with bad patterns, but it’s never been easier to simply remove those bad patterns from the codebase. Bad patterns within your codebase are largely a problem you can choose not to have at this point. This further simplifies operating decentralized teams, especially if you love introducing tighter typing and linting. Which I do.
One high context engineer can scale themselves across a team’s scope much more easily than before by using LLM-tooling to investigate, debug, monitor and influence agent development.
The broader this engineer’s judgment extends, the more effective they’ll become. I don’t think that any of design or product or engineering are going away, but individuals whose judgment spans across multiple of these fields are increasingly impactful on their teams because they can breeze through the easy cross-domain problems rather than get hung up on them.
In aggregate, I believe that these changes in degree become a change in kind for running an empowered team.
Minimize cross-functional dependencies with relationships or escalations
Even if code ownership is shared, it can become impossible to move forward if there’s enough cross-functional permission required to make changes. To a small extent this is due to inherent misalignment in incentives across functions, but it’s much more driven by incompatible priorities. Where possible, it’s ideal to embed functions into a common pod to align priorities, but in an era of lean teams, that’s not a particularly accessible solution.
Instead your two most likely solutions are the same ones that have always been there. First, building relationships across functions to help get your project unblocked by a helping hand from within that function. Second, an effective executive team is able to make collective tradeoffs–and share some of their treasury of cross-functional relationship merit–to unblock a given project.
Now, however, I think we finally have a playbook for speeding up cross-functional approval in a meaningful way, which is providing function-specific harnesses that can perform elements of their function’s work, especially of the approval variety. I don’t see substantial adoption of this technique yet, but I see it coming, and Imprint has spun up an internal AI team to iterate our way through deploying this approach. Hopefully we’ll be writing about our learning there in a month or two.
Our goal is that every function has a centrally managed harness that can perform an increasingly large portion of their team’s work, prioritizing the sorts of things that require cross-team approvals. They would have a collection of general tools–e.g. search indexes of their work corpus–and also very function specific tools–e.g. access to a tool like Zip for financial approvals on the Finance team–along with the ability to provide skills and direction to the harness. Assuming this idea works, this harness will become a shim on top of functions to further allow teams to focus on the highest value work rather than getting caught up in the least interesting approvals.
Eliminate decisions through the iterative application of judgment
With less accidental complexity slowing down our organizations from cross-team and cross-functional dependencies, we still have one very large remaining: deciding what software to write. Software is not, as some argue today, free, but it’s no longer constrained on implementation weeks but instead on correct decisions.
Grounding this in an example, for an upcoming release we found ourselves grappling with implications of changing how applicants applied for a new credit card. There were concerns that we didn’t have enough weeks to implement the change. However, the actual work to be done was roughly two weeks, and we were talking about the initiative taking two months. The gap between the two weeks and the two months was a dozen ambiguous decisions. Those proposing the longer timeline assumed we’d continue making decisions at the current decision-making pace, resulting in the longer schedule.
There was some discussion around how we might find that number of months to complete the project. Treating decision-making speed as a fixed constant oriented the discussion around a complex, low-value workstream of deciding the relative priority of other tasks we were currently working on and then reranking them to free up more calendar time. But we were never constrained on calendar time, what mattered was accelerating the pace of high-quality decision-making.
In my experience, calendar discussions are another way that folks deliberately get stuck, because no company ever has a central list of the true priority of every task. Asking for that non-existent list shifts accountability for forward progress off the asker, but it’s a fundamentally unreasonable thing to ask for. Thinking about things from a CPU scheduling perspective: any scheduler that tried to centrally orchestrate priority within every thread would be extraordinarily slow and stuck all the time; it’s only by locally determining priority–often through extremely crude methods–that things move quickly overall.
Decision-making at a fixed degree of complexity hasn’t gotten easier with AI-tooling but AI-tooling has dramatically reduced the complexity of most decisions I see in practice. Going back to the passkey problem, trying to make those decisions from a blank slate was very difficult. I was simply too ignorant of the handshake’s constraints to correctly decide the UX details. However, I was able to iterate through the open decisions as a side project, until the answers became obvious. The answers became obvious not because I got smarter–I didn’t get smarter–but because iterative exploration discarded many of the initial options until the inherent constraints of passkeys and our user experience simplified the problem.
Prototyping–e.g. the iterative application of judgment–can turn most complex problems into a series of simple problems, and the rate of prototyping today makes it a substantially more accessible strategy than in the past. Yes, prototyping has always been a well-received solution to validate ideas, but it used to be expensive enough that many teams didn’t take advantage of it, but these days I think it is the most accessible way to find high-quality decisions within an ambiguous problem space.
A roadmap of decisions, not on dates
Combining all of these ideas, the world we live in is increasingly constrained by access to high-quality decisions. Rather than talk about scheduling software around software, we should be fixated on the idea of accelerating making high-quality decisions. Every hour we spend deciding priorities or timelines is a distraction from making the next decision.
As an ending caveat, this isn’t an argument that we shouldn’t have dates on projects. Dates are the external interface for coordinating across companies, so you still need dates. However, I think teams are better picking a plausible, slightly conservative external date and then entirely ignoring that date internally. Instead, they shuold focus on the list of decisions to be made, and work through them directly. Dates, global priorities, stack ranked items… An engineer who doesn’t know the release date can still ship.