<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Jack Kora]]></title><description><![CDATA[Reflections on AI driven development, the tools we build around it, and how teams need to adopt to use it well.]]></description><link>https://jackkora.com</link><image><url>https://substackcdn.com/image/fetch/$s_!pYSy!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7bf18f0-b624-44ad-b45b-80265d54ecbd_1280x1280.png</url><title>Jack Kora</title><link>https://jackkora.com</link></image><generator>Substack</generator><lastBuildDate>Sat, 08 Aug 2026 23:53:07 GMT</lastBuildDate><atom:link href="https://jackkora.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Jack Kora]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[jackkora@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[jackkora@substack.com]]></itunes:email><itunes:name><![CDATA[Jack Kora]]></itunes:name></itunes:owner><itunes:author><![CDATA[Jack Kora]]></itunes:author><googleplay:owner><![CDATA[jackkora@substack.com]]></googleplay:owner><googleplay:email><![CDATA[jackkora@substack.com]]></googleplay:email><googleplay:author><![CDATA[Jack Kora]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Your architecture is only as real as your repo harness]]></title><description><![CDATA[Learn how a repo harness turns architecture, module boundaries, contracts, and engineering rules into automated checks that prevent codebase drift.]]></description><link>https://jackkora.com/p/architecture-repo-harness</link><guid isPermaLink="false">https://jackkora.com/p/architecture-repo-harness</guid><dc:creator><![CDATA[Jack Kora]]></dc:creator><pubDate>Fri, 07 Aug 2026 03:12:54 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!cXk1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82931f3e-cddc-42a2-9268-c502f6d42979_1920x1397.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cXk1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82931f3e-cddc-42a2-9268-c502f6d42979_1920x1397.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cXk1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82931f3e-cddc-42a2-9268-c502f6d42979_1920x1397.jpeg 424w, https://substackcdn.com/image/fetch/$s_!cXk1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82931f3e-cddc-42a2-9268-c502f6d42979_1920x1397.jpeg 848w, https://substackcdn.com/image/fetch/$s_!cXk1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82931f3e-cddc-42a2-9268-c502f6d42979_1920x1397.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!cXk1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82931f3e-cddc-42a2-9268-c502f6d42979_1920x1397.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cXk1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82931f3e-cddc-42a2-9268-c502f6d42979_1920x1397.jpeg" width="1456" height="1059" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/82931f3e-cddc-42a2-9268-c502f6d42979_1920x1397.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1059,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Electrical Wiring Harness | Electrical Harness Manufacturers | Industrial Wiring  Harness&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Electrical Wiring Harness | Electrical Harness Manufacturers | Industrial Wiring  Harness" title="Electrical Wiring Harness | Electrical Harness Manufacturers | Industrial Wiring  Harness" srcset="https://substackcdn.com/image/fetch/$s_!cXk1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82931f3e-cddc-42a2-9268-c502f6d42979_1920x1397.jpeg 424w, https://substackcdn.com/image/fetch/$s_!cXk1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82931f3e-cddc-42a2-9268-c502f6d42979_1920x1397.jpeg 848w, https://substackcdn.com/image/fetch/$s_!cXk1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82931f3e-cddc-42a2-9268-c502f6d42979_1920x1397.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!cXk1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82931f3e-cddc-42a2-9268-c502f6d42979_1920x1397.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#8220;Harness&#8221; is still a new and loosely defined term. Some people use it to describe the system around a coding agent; others use it for the tools and checks around a repository. Here is how I define <em>harness</em> in two distinct ways:</p><ul><li><p>A coding-agent harness is the deterministic logic around the LLM: the planning loop, tool calls, and memory architecture in systems like Claude Code and Codex CLI.</p></li><li><p>A repo harness is the collection of tools and automated checks that verifies a repository&#8217;s correctness&#8212;including its behavior, architecture, contracts, and engineering standards.</p></li></ul><p>This article is about the repo harness.</p><p>In <a href="https://jackkora.com/p/beyond-2x-ai-productivity-boost-architecture">Beyond 2x AI productivity boost: Architecture is the missing piece</a>, I described an architecture organized across system, application, and module boundaries. A repo harness makes that architecture enforceable: it turns those boundaries, public interfaces, contracts, and other rules into checks that reject violations before the change is accepted.</p><h2>How a repo harness verifies correctness</h2><p>A repo harness is there to verify repository correctness. So what does correctness mean? At a high level, it has two dimensions: behavioral correctness and repository conformance.</p><p>Behavioral correctness asks whether the software does what it is supposed to do. Teams build confidence in behavioral correctness through the familiar automated testing pyramid: many unit tests, fewer service and integration tests, and a small number of end-to-end tests. Testing strategy is not a new topic, and it is well covered elsewhere, so I will not revisit it here. The important point for this discussion is that the automated test suite is an important part of the repo harness.</p><p>Repository conformance&#8212;or repo conformance&#8212;asks whether a change preserves the intended shape and constraints of the repository: module boundaries, dependency direction, ownership, public interfaces, contracts, type safety, security requirements, and project-specific rules. Conformance violations can leave the software behaving correctly today while making the codebase harder and less safe to change tomorrow. This is the less familiar part of the harness&#8212;and where this post will focus.</p><blockquote><p>AI is really good at patterns. Give it a good pattern and it will create more of it. Give it just one bad pattern and it will proliferate through the codebase. This is why it&#8217;s so important to ensure repo conformance upfront.</p></blockquote><p>The checks vary, but reliable verification requires the same basic ingredients.</p><p><strong>A precise rule.</strong> &#8220;Keep the code clean&#8221; is guidance. &#8220;Only this module may access these tables&#8221; is enforceable.</p><p><strong>An observable violation.</strong> The repo harness needs evidence it can inspect. A behavioral test may compare an output or durable state with the expected result. A structural check may detect an illegal import, dependency cycle, foreign table access, or drifted contract.</p><p><strong>An automated checker.</strong> Enforcement cannot depend on someone remembering the rule during review.</p><p><strong>One command and a deterministic consequence.</strong> The same command should run for a developer, a coding agent, and remote CI. A violation should fail clearly, identify the broken rule, and point toward the fix.</p><p><strong>Tests for custom enforcement code.</strong> Custom checkers and generators are code, so they need automated tests like any other code. A checker test should include a known violation and prove that the checker rejects it. A clean repository passing only proves that the current repository passes; it does not prove that the checker works.</p><h2>A harness combines 3rd-party tools with repository-owned checks</h2><p>A repo harness is not one product, and most of it does not need to be built from scratch. It usually combines general-purpose 3rd-party tools with tests, configuration, and custom checks owned by the repository.</p><p><strong>3rd-party tools provide the general-purpose capabilities.</strong> Test runners, linters, type checkers, dependency analyzers, security scanners, migration tools, and dead-code detectors already solve common verification problems. Use an existing tool when it can reliably enforce the property you care about.</p><p><strong>Repository-owned verification provides the local rules and fills the gaps.</strong> Automated tests encode the application&#8217;s expected behavior. Tool configuration, custom checkers, and generators encode repo-specific constraints such as dependency direction, module boundaries, table ownership, public-interface restrictions, and generated contract consistency. When an existing tool cannot express a constraint reliably, write a narrow custom checker or generator.</p><h2>The harness is language-specific</h2><p>Part of a repo harness exists to enforce guarantees that a language does not provide on its own. The same architectural rule can require different enforcement depending on the stack.</p><p>This matters especially with AI agents. An agent will often take the shortest valid path to complete a task. It does not treat a convention as a hard boundary simply because the convention is documented. If the language permits a shortcut, an agent will eventually use it.</p><p>Consider module privacy. In Python, a leading underscore marks a name as internal, but that is only a convention. Nothing prevents another module from importing it directly. If that boundary matters, the harness has to enforce it.</p><p>Java provides stronger access control. Private and package-private members are enforced by the compiler, and the Java module system can restrict which packages are exported. When the boundary is represented correctly, ordinary code outside it cannot reach those internals. In that case, the compiler and module system are already enforcing part of the harness.</p><p>Dependency cycles provide another example. Go rejects cyclic package imports during compilation. Python and TypeScript do not enforce an intended acyclic module architecture, so repositories using them may need additional dependency-analysis tooling.</p><p>There is no universal implementation of a repo harness. Start with the guarantees the language, compiler, and type system already provide. Then use linters, dependency analyzers, tests, or custom checks to close the gaps that matter to the repository.</p><h2>The harness is never complete</h2><p>A strong baseline is mandatory. Every repository needs the basic checks appropriate to its stack: automated tests, linting, type checking, security scanning, dependency hygiene, and build validation. These checks catch broad classes of problems and should be present from the beginning. But they are only a starting point.</p><p>Repo-specific checks are equally important. A payments application may need to enforce tenant and ledger isolation. A data platform may need schema-compatibility checks. General-purpose tooling cannot know which properties matter to a particular system.</p><p>Higher development speed produces feedback faster. When that feedback exposes a gap in the architecture, the code and harness should evolve together.</p><p>A postmortem should therefore ask not only, &#8220;What code allowed this failure?&#8221; but also, &#8220;What gap in the harness allowed this change to be accepted?&#8221; The fix may include a code change, a new conformance rule, a checker and tests for that checker, or clearer documentation of the repository&#8217;s constraints.</p><p>That does not mean mechanizing every preference. A noisy or ambiguous check teaches people to ignore the harness. Add enforcement when the property is important, the violation is observable, and the signal can be reliable.</p><h2>Some drift is visible only over time</h2><p>Per-change checks catch violations of rules the repository already knows. But some forms of degradation accumulate slowly. No individual change is clearly wrong and the pattern becomes visible only across many changes.</p><p>Module dependencies may all be legal while the graph becomes increasingly dense. Public interfaces, suppression allowlists, test runtimes, and artifact sizes may grow a little at a time. Domain concepts may acquire duplicate names and representations. External conditions also change: a dependency can gain a vulnerability without anyone touching the repository.</p><p>These properties are better evaluated by recurring checks. Deterministic checks can run as scheduled jobs and fail against an explicit threshold or baseline. More subjective concerns, such as domain model quality, can produce a report for periodic architectural review.</p><p>Recurring checks should not become a dumping ground for noisy analysis. If a check is precise, reliable, and cheap enough to run on every change, it belongs in the normal harness. If a recurring review reveals a repeatable failure mode, the next step is to turn that lesson into a deterministic rule wherever possible.</p><div><hr></div><p>This is really it&#8212;a repo harness is not a complicated concept: establish a strong baseline, add the conformance checks your system needs, run them consistently, use recurring checks to catch slower drift, and evolve the harness as the codebase teaches you.</p><p>To make it all tangible, the appendix below catalogs representative checks generalized from my real-world experience, along with a few recent ideas.</p><h1>Appendix: Sample harness checks</h1><p>This is not a complete list of harness checks. These examples focus on less obvious forms of repo conformance: checks that prevent architectural drift and repository-specific failures ordinary tooling will not catch.</p><h2>System level</h2><h3>The repository has an explicit directory structure</h3><p><strong>Problem.</strong> Developers and coding agents create new top-level folders or one-off directory shapes whenever the existing structure is not immediately obvious. Over time, the repository stops communicating where code belongs.</p><p><strong>Rule.</strong> Tracked files must fit a declared directory structure. Adding a new structural concept requires an explicit change to that definition.</p><p><strong>How.</strong></p><ul><li><p>Describe the permitted tree in a machine-readable manifest or the repository&#8217;s existing build and workspace configuration.</p></li><li><p>Compare tracked paths with that structure and fail on unknown structural paths.</p></li><li><p>If this requires a custom checker, test it with a known invalid path.</p></li></ul><h3>Cross-app contracts cannot drift</h3><p><strong>Problem.</strong> A contract is often represented independently by several applications or languages. A producer can change while a consumer, generated artifact, or hand-written representation silently remains on the old shape.</p><p><strong>Rule.</strong> Every cross-app contract must stay synchronized across its producer, consumers, and committed schema. This includes REST APIs, queue and event payloads, WebSocket or SSE messages, webhook bodies, shared file formats, and custom cross-language protocols.</p><p><strong>How.</strong></p><ul><li><p>In a code-first design, generate a canonical OpenAPI, AsyncAPI, Protobuf, JSON Schema, or equivalent artifact from the producer and commit it.</p></li><li><p>Regenerate that artifact in CI and fail if it differs from the committed version.</p></li><li><p>Generate each consumer&#8217;s internal types from the artifact and check those generated results for drift as well.</p></li><li><p>In a schema-first design, make the committed schema authoritative and generate both sides from it. If a representation must remain handwritten, compare it with the schema or test the contract directly.</p></li><li><p><a href="https://openapi-generator.tech/docs/generators">OpenAPI Generator</a> supports Java, Python, and TypeScript, while <a href="https://buf.build/docs/breaking/">Buf</a> can check Protobuf compatibility. Independently deployed consumers may need compatibility checks in addition to drift checks.</p></li></ul><h2>App level</h2><h3>Every public entry point declares its cross-cutting policy</h3><blockquote><p>This is one of the most important examples in this appendix. It demonstrates how you can enforce an important goal through complementary architecture &amp; harness implementations. Here architecture reduces a complex cross-cutting requirement to a single declaration mechanism. And the harness verifies that this declaration mechanism is used on every entry point.</p></blockquote><p><strong>Problem.</strong> Authentication, authorization and permissions, auditing, and observability are easy to apply inconsistently when every endpoint or module operation handles them independently. A new entry point can omit a concern entirely or enforce it differently from the rest of the application.</p><p><strong>Rule.</strong> Every endpoint and public module operation must use the application&#8217;s standard entry-point declaration. The declaration explicitly states the policies that vary by operation, while the runtime automatically applies the concerns that are universal.</p><p>For example:</p><pre><code><code>@endpoint(
    url="/cart",
    authn=SignedIn,
    permissions=[ViewCart, ViewPaymentMethods],
    audit=False,
)
</code></code></pre><p><strong>How.</strong></p><ul><li><p>Provide one standard declaration mechanism: a decorator, annotation, attribute, or typed registration object, depending on the language and framework.</p></li><li><p>Require explicit values for policies such as authentication mode, permissions, and auditing. Omission cannot silently mean &#8220;unprotected.&#8221;</p></li><li><p>Route invocations through shared runtime infrastructure that interprets the declaration and applies authentication, authorization, auditing, observability, and other cross-cutting behavior.</p></li><li><p>Have the harness inspect every registered endpoint and declared module interface and fail if the required policy metadata is missing.</p></li><li><p>Test both pieces: checker tests prove that missing metadata fails the harness, while behavioral tests prove that the shared runtime infrastructure enforces each declared policy.</p></li></ul><p>The harness guarantees that every entry point declares a policy and that the declared policy is enforced. Choosing the correct permission remains a policy-design and testing problem.</p><h3>Test-only code cannot enter production</h3><p><strong>Problem.</strong> Reset endpoints, seed helpers, fake controls, test imports, or environment overrides can become reachable in a production artifact even though the test suite itself works correctly.</p><p><strong>Rule.</strong> Test support may exist in a controlled test topology but must be absent from production dependencies, routes, contracts, and build outputs.</p><p><strong>How.</strong></p><ul><li><p>Isolate test support with mechanisms such as Java test source sets, dedicated Python test packages, or separate TypeScript entry points.</p></li><li><p>Prevent production code from depending on that source layer and exclude it from production builds.</p></li><li><p>Inspect the production artifact&#8217;s routes, imports, bundled files, or generated contracts for test-only capabilities.</p></li></ul><h3>Module dependencies and public interfaces follow the architecture</h3><p><strong>Problem.</strong> As an application grows, modules begin depending on modules they should not know about, form cycles, or bypass a module&#8217;s public interface by importing its internals.</p><p><strong>Rule.</strong> Cross-module dependencies must follow permitted directions, remain acyclic when required by the architecture, and target only interfaces deliberately exposed by the owning module.</p><p><strong>How.</strong></p><ul><li><p>Prefer native module and package visibility when the language provides the required guarantees.</p></li><li><p>Otherwise, use dependency analysis to reject forbidden edges, cycles, and imports of internal files or symbols.</p></li><li><p>Examples include <a href="https://docs.gauge.sh/getting-started/introduction/">Tach</a> for Python, <a href="https://www.archunit.org/userguide/html/000_Index.html">ArchUnit</a> for Java, and <a href="https://github.com/sverweij/dependency-cruiser/blob/main/doc/rules-reference.md">dependency-cruiser</a> for TypeScript and JavaScript.</p></li><li><p>In dynamic languages, keep the public surface statically discoverable so enforcement tools do not need to execute application code.</p></li><li><p>Account for gaps such as dynamic imports and reflection with a custom check or an explicit restriction.</p></li></ul><h3>Database models and migrations cannot drift</h3><p><strong>Problem.</strong> Application models can change without a migration, migrations can diverge from the models, or a migration history can fail when applied from an empty database.</p><p><strong>Rule.</strong> The migration history must produce the database schema expected by the current application.</p><p><strong>How.</strong></p><ul><li><p>Apply the complete migration history to a disposable database.</p></li><li><p>Derive the expected schema from the application&#8217;s current model or schema definition.</p></li><li><p>Compare the two schemas and fail on meaningful differences.</p></li><li><p>If applications run migrations during startup, separately test concurrent migration attempts.</p></li></ul><h3>Inline suppressions must be explicitly allowlisted</h3><p><strong>Problem.</strong> Inline directives such as &#8220;noqa,&#8221; &#8220;type: ignore,&#8221; &#8220;eslint-disable,&#8221; &#8220;ts-ignore,&#8221; &#8220;nolint,&#8221; or &#8220;SuppressWarnings&#8221; are sometimes necessary, especially in modules doing technically unusual work. But an untracked suppression can also bypass an important check and then be copied without its original context.</p><p><strong>Rule.</strong> Every inline suppression must appear in a small, committed allowlist. An unlisted suppression fails the harness.</p><p><strong>How.</strong></p><ul><li><p>Keep the approved suppressions in a simple text file, identified by their source location and directive.</p></li><li><p>Use a repository script to scan the source tree, normalize the suppressions it finds, and compare them with the allowlist.</p></li><li><p>Fail on both new suppressions and stale allowlist entries.</p></li><li><p>Keep the list small&#8212;likely dozens of entries concentrated in a few unusual modules&#8212;so every addition receives explicit review.</p></li><li><p>Test the script with an unlisted suppression and a stale allowlist entry.</p></li></ul><h2>Module level</h2><h3>Persistence models stay inside the module that owns them</h3><p><strong>Problem.</strong> When ORM entities or persistence records cross a module boundary, callers can become coupled to database columns, relationships, lazy-loading behavior, or session lifecycle. The module no longer owns its persistence implementation in practice.</p><p><strong>Rule.</strong> When persistence is intended to be a module implementation detail, public interfaces expose domain values or DTOs rather than ORM entities.</p><p><strong>How.</strong></p><ul><li><p>Use native visibility where the language can keep persistence types inside their owning module.</p></li><li><p>Otherwise, use architecture tests, import rules, or public-signature analysis to detect references to known persistence types.</p></li><li><p>Tailor the check to the ORM and type system rather than assuming one technique works everywhere.</p></li><li><p>If persistence models are deliberately shared contracts, encode that architecture instead of prohibiting them.</p></li></ul><h3>Module interfaces do not expose module-owned mutable state</h3><p><strong>Problem.</strong> Exporting a registry, cache, ambient context object, mutable collection, or singleton accessor lets callers read or change state outside the behavior owned by the module.</p><p><strong>Rule.</strong> A module exposes behavior and immutable values, not direct access to the mutable state it owns.</p><p><strong>How.</strong></p><ul><li><p>Use access modifiers, immutable interfaces, or ownership types where the language provides them.</p></li><li><p>Otherwise, inspect exported bindings and public return types with architecture tests or a custom check.</p></li><li><p>Target concrete ways state can escape&#8212;such as exported instances, mutable collections, or singleton accessors&#8212;rather than treating every class instance or collection as invalid.</p></li></ul><h3>Every database table has one owning module</h3><p><strong>Problem.</strong> A module can bypass another module&#8217;s interface by importing its persistence model or issuing SQL directly against its tables. The code may still work while ownership quietly disappears.</p><p><strong>Rule.</strong> If the architecture assigns table ownership, only the owning module may access that table directly.</p><p><strong>How.</strong></p><ul><li><p>Derive or declare which module owns each table.</p></li><li><p>Use dependency rules to prevent modules from importing another owner&#8217;s ORM models.</p></li><li><p>Where reliable, analyze literal SQL for references to tables owned by another module.</p></li><li><p>Enforce only the query forms the checker can inspect accurately, and centralize infrastructure exceptions such as migrations.</p></li></ul>]]></content:encoded></item><item><title><![CDATA[Beyond 2x AI productivity boost: Architecture is the missing piece]]></title><description><![CDATA[Learn how explicit architecture, module boundaries, and automated enforcement help teams move far beyond 2x productivity with AI coding agents.]]></description><link>https://jackkora.com/p/beyond-2x-ai-productivity-boost-architecture</link><guid isPermaLink="false">https://jackkora.com/p/beyond-2x-ai-productivity-boost-architecture</guid><dc:creator><![CDATA[Jack Kora]]></dc:creator><pubDate>Thu, 30 Jul 2026 20:39:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Mxrz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384f6bc-d19d-49f7-9155-a4bdc00c6135_1500x750.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Mxrz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384f6bc-d19d-49f7-9155-a4bdc00c6135_1500x750.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Mxrz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384f6bc-d19d-49f7-9155-a4bdc00c6135_1500x750.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Mxrz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384f6bc-d19d-49f7-9155-a4bdc00c6135_1500x750.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Mxrz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384f6bc-d19d-49f7-9155-a4bdc00c6135_1500x750.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Mxrz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384f6bc-d19d-49f7-9155-a4bdc00c6135_1500x750.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Mxrz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384f6bc-d19d-49f7-9155-a4bdc00c6135_1500x750.jpeg" width="1456" height="728" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d384f6bc-d19d-49f7-9155-a4bdc00c6135_1500x750.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:728,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;What Are The 7 Different Types Of Architecture? - Immerse Education&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="What Are The 7 Different Types Of Architecture? - Immerse Education" title="What Are The 7 Different Types Of Architecture? - Immerse Education" srcset="https://substackcdn.com/image/fetch/$s_!Mxrz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384f6bc-d19d-49f7-9155-a4bdc00c6135_1500x750.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Mxrz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384f6bc-d19d-49f7-9155-a4bdc00c6135_1500x750.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Mxrz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384f6bc-d19d-49f7-9155-a4bdc00c6135_1500x750.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Mxrz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384f6bc-d19d-49f7-9155-a4bdc00c6135_1500x750.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Most teams can get a meaningful productivity boost from AI coding tools without changing their architecture. Agents write boilerplate, explain unfamiliar code, and accelerate local implementations. That is the 2x version: coding tasks get faster. The 5x version is not just faster code generation; it is about safely delegating much larger units of work.</p><blockquote><p>I use 2x and 5x to describe the difference in leverage. They are not precise benchmarks, although they are directionally consistent with informal industry reports.</p></blockquote><p>That requires moving up an abstraction layer. Instead of spending most of the day manipulating code, a developer describes goals, constraints, architecture, and tradeoffs while the agent handles more implementation. This sounds like a tooling change. It is actually an architecture problem.</p><p>A developer can compensate for a confusing codebase by learning its hidden conventions. An agent starts with none of that institutional memory. If the architecture is implicit, it must reconstruct it&#8212;or guess. Agents are great at repeating the patterns they find: in a clean codebase that is desirable; in a legacy codebase it produces legacy code faster.</p><div class="callout-block" data-callout="true"><p>This architecture is not theoretical. I introduced it at a recent job and watched it remain stable in production for a year. We did not need to refactor the high-level shape; core modules stayed stable and reusable and domain code remained largely free of technical plumbing. And product work mostly meant adding or evolving domain modules.</p></div><h1>Architecture is a system of complementary patterns</h1><p>What is architecture? My working definition is pretty simple. <strong>Architecture is a system of complementary patterns.</strong></p><p>Both <strong>system</strong> and <strong>complementary</strong> are important. A codebase can contain dependency injection, repositories, services, events, tests, and clean interfaces without those patterns composing into coherent architecture.</p><p>Good architecture answers the same questions consistently:</p><ul><li><p>Where does this behavior belong?</p></li><li><p>Who owns this data?</p></li><li><p>How may another part of the system use it?</p></li><li><p>What context is required to change it?</p></li><li><p>How do we know the change is safe?</p></li><li><p>Which rules require judgment, and which can be enforced?</p></li></ul><p>The answers create architectural seams: the places where parts of the system meet&#8212;and where we can enforce rules. A module interface is a seam. So is an app boundary, API contract, database ownership rule, or the line between production code and system tests.</p><p>Those seams also become the language for discussing change. &#8220;Create a <code>returns</code> domain module and add a <code>get_returnable_items</code> operation to the <code>orders</code> module&#8217;s public interface&#8221; tells an engineer or agent where behavior belongs and which boundary may change. That is far more useful than &#8220;add return functionality.&#8221;</p><p>AI is excellent at repeating patterns, which makes consistency unusually valuable. Humans must own and approve the seams, although agents can increasingly propose them. Without explicit human ownership, an agent may make local architectural choices without the global purview needed for system-level design.</p><h1>The goals</h1><p>Before getting into the repository shape, here are the four goals of this architecture.</p><p><strong>Human-directed and agent-legible.</strong> Humans own goals, constraints, boundaries, and judgment. The repository makes those decisions explicit enough for an agent to act without reconstructing hidden conventions.</p><p><strong>Locally understandable and changeable.</strong> Most changes should require understanding one bounded area and its explicit dependencies, not the whole system.</p><p><strong>Clear ownership, automatically enforced.</strong> Every behavior, contract, and piece of state has one owner. And CI rejects violations of critical architecture rules.</p><p><strong>Safe to change and govern.</strong> Stable seams provide consistent hooks for cross-cutting policy and allow parts of the system to evolve independently where their boundaries permit.</p><p>These are not AI-only goals. They also reduce human onboarding time, review scope, accidental coupling, and knowledge trapped in senior engineers&#8217; heads.</p><blockquote><p>Historically, maintaining this level of consistency required substantial communication and oversight, so it was most common in large engineering organizations. AI agents and executable harnesses now make the same discipline accessible to much smaller teams.</p></blockquote><h1>Architecture, disassembled</h1><h2>Show me the map</h2><pre><code><code>repo/
&#9500;&#9472;&#9472; apps/
&#9474;   &#9500;&#9472;&#9472; app1/
&#9474;   &#9474;   &#9500;&#9472;&#9472; app/
&#9474;   &#9474;   &#9474;   &#9500;&#9472;&#9472; core/
&#9474;   &#9474;   &#9474;   &#9474;   &#9500;&#9472;&#9472; module1/
&#9474;   &#9474;   &#9474;   &#9474;   &#9492;&#9472;&#9472; module2/
&#9474;   &#9474;   &#9474;   &#9492;&#9472;&#9472; domain/
&#9474;   &#9474;   &#9474;       &#9500;&#9472;&#9472; module1/
&#9474;   &#9474;   &#9474;       &#9492;&#9472;&#9472; module2/
&#9474;   &#9474;   &#9500;&#9472;&#9472; bin/
&#9474;   &#9474;   &#9500;&#9472;&#9472; docs/
&#9474;   &#9474;   &#9500;&#9472;&#9472; harness/
&#9474;   &#9474;   &#9500;&#9472;&#9472; db_migrations/
&#9474;   &#9474;   &#9492;&#9472;&#9472; test_support/
&#9474;   &#9492;&#9472;&#9472; app2/
&#9500;&#9472;&#9472; shared/
&#9474;   &#9492;&#9472;&#9472; &lt;language&gt;/
&#9474;       &#9500;&#9472;&#9472; shared/core/
&#9474;       &#9500;&#9472;&#9472; bin/
&#9474;       &#9500;&#9472;&#9472; docs/
&#9474;       &#9492;&#9472;&#9472; harness/
&#9500;&#9472;&#9472; e2e/
&#9474;   &#9500;&#9472;&#9472; tests/
&#9474;   &#9500;&#9472;&#9472; test_services/
&#9474;   &#9500;&#9472;&#9472; bin/
&#9474;   &#9492;&#9472;&#9472; docs/
&#9500;&#9472;&#9472; bin/
&#9500;&#9472;&#9472; docs/
&#9500;&#9472;&#9472; harness/
&#9500;&#9472;&#9472; AGENTS.md
&#9492;&#9472;&#9472; README.md
</code></code></pre><p>The directory tree is not the architecture itself, but it is a useful projection of it. At a glance, a human or agent can infer whether code is system-wide, app-owned, reusable, business logic, technical infrastructure, or test-only. The path communicates likely ownership, dependencies, documentation, tests, and governing policy.</p><p>One pattern repeats across the major scopes: <strong>bin, docs, and harness</strong>.</p><ul><li><p><strong>bin</strong> is the stable command surface for humans, agents, and CI.</p></li><li><p><strong>docs</strong> explains the architecture and workflows owned by that scope.</p></li><li><p><strong>harness</strong> implements the checks, generators, and dependency analysis.</p></li></ul><p>The trio appears at repository, shared-language, and app scopes. Modules are leaf scopes: each contains its implementation, tests, and docs, but does not duplicate the command and harness platforms.</p><p>Everything has a place, and the place communicates meaning.</p><h2>Apps, services, and modules are different boundaries</h2><p>These terms are often blurred, leading to architecture driven by deployment topology rather than software cohesion. Code that belongs together may be separated simply because it runs in different processes.</p><ul><li><p>A <strong>service</strong> is an independently deployable runtime boundary. It may run on infrastructure you operate&#8212;such as an API, worker, or scheduler&#8212;or in a client environment, such as a separately deployed SPA.</p></li><li><p>An <strong>app</strong> is a durable code, build, and data-ownership boundary. It has its own build target and CI scope and, when it stores data, usually its own database and migration lifecycle.</p></li><li><p>A <strong>module</strong> is a bounded package of production code for one capability. It belongs to an app or a shared-language scope and exposes a public module interface. In practice, it owns one job&#8212;billing, reviews, authentication, etc.</p></li></ul><p>One app may produce an API, worker, scheduler, and event consumer services. They can scale and deploy differently while reusing the same modules and data ownership.</p><p>Splitting out a new app is a big commitment. If the boundary is not obvious, keep the capability as a module. You can still run it as a separate service when it needs independent scaling or failure isolation. A well-structured module is easy to extract later; prematurely split apps are painful to stitch back together.</p><p>The same distinction applies on the frontend. A feature area within an SPA is usually a module. An independently built and deployed SPA is a service boundary. Create a separate frontend app only when it represents a durable product, code, and ownership boundary&#8212;not merely a new persona, screen, or route. Micro-frontends are an operational choice, not the default module boundary.</p><h2>One repository</h2><p>As you may have guessed already, this is a monorepo of independently bounded apps, reusable same-language core modules, and system-level support.</p><p>This is not limited to server code. Web, mobile, ML, and other production applications belong in the same monorepo when they meet the app boundary, even when they use different languages and toolchains.</p><p>The monorepo is the envelope, not the core idea. It gives an agent one system map, allows atomic cross-app contract changes, and gives shared enforcement one home.</p><p>The monorepo provides visibility. The app boundaries provide independence.</p><blockquote><p>Modern build and CI/CD tools provide first-class monorepo support, making builds and deployments much easier to manage than they were a decade ago.</p></blockquote><h2>Shared is not a miscellaneous drawer</h2><p>Code reused by several apps is grouped by language under <strong>shared</strong>.</p><blockquote><p>Shared is not a home for generic <code>utils</code> or <code>helpers</code>; catch-all modules obscure ownership and become dumping grounds.</p></blockquote><p>Shared contains reusable core capabilities, not app-specific workflows or domain modules. Similar business concepts may remain separate when their rules and ownership differ.</p><p>Each app consumes only the shared modules it needs through explicit local dependencies. Shared modules are linked directly during each app&#8217;s build; the team does not publish them as internal packages.</p><p>The language-specific mechanism can vary. The invariants do not:</p><ul><li><p>The dependency is explicit.</p></li><li><p>The module keeps a stable namespace and owner.</p></li><li><p>The same boundary checks apply.</p></li></ul><p>Module dependencies form an acyclic graph, with the repo harness rejecting any circular dependency.</p><h2>Modules are the primary unit of understanding</h2><p>Inside an app, modules are the primary architectural boundary. A module owns one cohesive capability, its public interface, implementation, tests, docs, and any tables it declares.</p><p>A <strong>domain module</strong> owns a business capability: its rules, workflows, and state. Its boundary also gives the module&#8217;s business language one consistent meaning&#8212;the domain-driven design (DDD) idea of a bounded context.</p><p>A <strong>core module</strong> owns a technical capability such as authentication, observability, database mechanics, messaging, auditing, etc.</p><p>Core does not have to mean domain-blind. Some core modules are <strong>domain-aware</strong>. Observability may describe tickets, accounts, runs, and actors. Auditing may record that a user changed an account.</p><p>A domain-aware core module may accept small typed domain values or references, as long as doing so does not make core import or depend on the domain module. But it does not own a business workflow or make a business decision.</p><blockquote><p>Domain-aware core uses business vocabulary to perform technical work. Domain owns business behavior.</p></blockquote><p>The appendix includes concrete examples of core and domain modules.</p><h2>Modules meet the system through explicit seams</h2><p>A seam is any boundary where a module or app exposes something others depend on, or interacts with something it does not own. Within an app, common seams include public code interfaces, endpoints, table definitions, and adapters. At app boundaries, APIs, events, queues, and other owned contracts are the seams. Apps do not import one another&#8217;s implementation or access one another&#8217;s databases.</p><p>Each seam has one owner and an explicit contract. Other modules use public interfaces rather than internal implementation; clients use module-owned endpoints; integrations pass through owned messages and adapters. Because seams have standard locations, the repo harness can enforce ownership, detect contract changes, attach policy, and identify affected consumers.</p><p><strong>Every table has exactly one owning module.</strong> Other modules use that module&#8217;s public interface rather than querying its tables directly.</p><p>Modules own their schemas and table access; the app coordinates them through one physical database and migration stream. This produces a modular monolith: in-process collaboration and one database where useful, without shared global state.</p><h2>Cross-module composition still needs an owner</h2><p>Explicit ownership creates an immediate question: if business logic lives in domain modules, where does behavior spanning several modules belong? Composition is not ownerless&#8212;the module accountable for the outcome coordinates the work through public interfaces.</p><p>A use case spanning several modules therefore belongs to the module that owns its outcome. For example, <code>returns</code> can retrieve order items through the <code>orders</code> interface, apply return-eligibility rules, and expose the assembled result.</p><p>Screen-specific combinations can be assembled by the frontend or by a thin client-facing query module when they need a dedicated server-side contract. Clients may assemble and present data, but authoritative business rules remain with the modules that own them. If a query becomes expensive or historical, its owner may maintain an event-fed read model.</p><p>Composition must use public interfaces&#8212;never another module&#8217;s tables or a generic aggregation module that collects unrelated use cases.</p><h2>Architecture inside a module</h2><p>Do modules need their own clean, hexagonal, or layered architecture? Sometimes, but not automatically.</p><p>Most modules should remain simple.</p><p>In the online store, <code>catalog</code> might remain a straightforward set of product operations over persistence. <code>orders</code>, with a multi-step lifecycle such as placement, payment, fulfillment, and cancellation, may benefit from separating state transitions and business rules from persistence and external integrations.</p><p>For a complex module, separating business logic from databases and external dependencies is usually the highest-value internal seam. If it still needs many layers, first ask whether it contains several capabilities and should become several modules. Add deeper architecture only when the remaining complexity is cohesive.</p><p>Architecture should go down to the level where local patterns are clear. Below that, the agent can figure out the implementation.</p><h2>Documentation and testing follow ownership</h2><p>Docs are colocated with the scope they explain. Root docs own system behavior, app docs own cross-module flows, and module docs own the module&#8217;s boundary and invariants. Parent docs link downward instead of repeating facts. One fact lives in one place.</p><p>Context discovery follows the same structure. For a changed path, it returns the smallest complete doc set. Root context is included for system work, not every local change; irrelevant context can be as harmful as missing context.</p><p>Testing follows ownership too:</p><ul><li><p>Module tests verify behavior inside one module.</p></li><li><p>App-level test support verifies flows crossing modules through public interfaces.</p></li><li><p>Top-level E2E tests verify the assembled system through the browser, HTTP, and events.</p></li><li><p>Purpose-built fake external services&#8212;such as a fake payment gateway or email provider&#8212;live with the E2E topology. This is why the <code>e2e</code> directory is top-level rather than another app: it owns verification of the assembled system.</p></li></ul><h2>The harness makes the map true</h2><p>The repo harness turns the architectural map from guidance into an enforced system.</p><p>In my earlier <a href="https://jackkora.com/p/a-framework-for-ai-dev-tooling-models">framework for AI dev tooling</a>, I separated the repo harness from the <strong>coding agent harness</strong>. A coding agent harness&#8212;such as Claude Code&#8212;is the loop around the model: planning, tool use, memory, and execution. The repo harness is specific to the repository and makes any coding agent effective inside it.</p><p>The repo harness has two sides.</p><p><strong>Deterministic.</strong> Scripts and hard checks enforce dependency direction, cycles, public-interface use, table ownership, tests, generated contracts, and other invariants. Violations fail before merge.</p><p><strong>Non-deterministic.</strong> Docs and AI skills are explicit, deterministic artifacts, but how an agent interprets and applies them is not. They help it load the right context, follow the intended workflow, and apply human-defined judgment where hard checks cannot.</p><p>The repo harness should not become a second, drifting architecture description. It treats the tree and build files as sources of truth:</p><ul><li><p>Convention establishes structure.</p></li><li><p>The harness derives structural facts&#8212;including module inventories, dependencies, context, contracts, and affected consumers&#8212;from the tree and build files.</p></li><li><p>Configuration is used only for choices that cannot be inferred, such as review requirements or risk classifications.</p></li></ul><p>The same dependency graph also supports CI/CD: changes can build and test the affected apps and consumers, while contract and migration changes receive additional verification. Deployment ownership, observability, rollback, and failure isolation attach to the same app and service boundaries.</p><h2>Seams are policy hooks</h2><p>Stable seams support more than dependency checks.</p><p>A module interface is a natural attachment point for tracing, audit logging, authorization, validation, and other cross-cutting policy. If all cross-module operations use instrumented entry points, every incident can at least be narrowed to a module.</p><p>The same paths support human ownership, reviews, alerts, dashboards, runbooks, reliability targets, and security responsibility.</p><p>They also support AI governance. A good software factory exposes hooks where teams decide how much autonomy an agent receives, and periodically checks whether the architecture still matches its intended shape. Recurring drift checks catch cumulative problems that no single change exposes&#8212;dependency creep, blurred ownership, stale docs, or missing enforcement. Their findings become refactoring work and, where possible, new deterministic checks. CODEOWNERS-style policy can govern authority, not just who does the code review.</p><p>For example:</p><ul><li><p>An internal module change with no interface change may proceed autonomously.</p></li><li><p>A module interface change or new dependency may require human review.</p></li><li><p>Contracts and migrations trigger extra verification.</p></li><li><p>Security or harness changes remain human-gated.</p></li></ul><p>This is better than reviewing every AI change manually or allowing unrestricted autonomy. The goal is high autonomy inside low-risk scopes, with escalation when a sensitive boundary or contract changes.</p><h2>Why this compounds</h2><p>Taken together, these patterns change the size and number of tasks that can be delegated safely.</p><p><strong>Cohesive modules reduce context.</strong> The agent reasons about one capability and its explicit dependencies.</p><p><strong>Standard shapes make patterns reliable.</strong> Nearby examples teach the right lesson.</p><p><strong>Explicit ownership bounds the blast radius.</strong> Interfaces and table ownership make impact clear.</p><p><strong>Executable rules enable autonomy.</strong> Humans need not supervise every import, table access, contract update, or documentation obligation.</p><p><strong>Context discovery reduces setup.</strong> The right docs and skills arrive with the task.</p><p><strong>The dependency graph makes verification complete.</strong> Shared changes trigger every affected consumer without testing unrelated code.</p><p><strong>Stable seams make governance cheap.</strong> Reviews, permissions, observability, auditing, and authorization use the same model.</p><p>These benefits compound: a local task is easier to implement, a standard change is easier to review, a bounded change is safer to automate, and targeted verification makes that automation faster.</p><p>Humans get the same map. New engineers navigate faster, reviewers focus on real impact, teams own modules without owning microservices, and incident responders narrow failures to stable boundaries.</p><p>This is not architecture optimized for AI at humans&#8217; expense. It is good architecture made explicit enough that AI can participate and that human devs equally benefit from as well.</p><h2>The architecture has to evolve at AI speed</h2><p>Some boundaries will be wrong, and modules will sometimes grow beyond what remains locally understandable. Faster implementation makes continuous architectural correction more important because structural problems can spread just as quickly as useful patterns.</p><p>When AI lets a team move 5x faster, refactoring also has to move 5x faster so architectural feedback keeps pace. I would expect 10&#8211;20% of development time to go toward architecture, the repo harness, and refactoring as a practical rule of thumb. When an agent makes a mistake a check could catch, add the check; when a rule repeatedly needs explanation, improve the docs or skill; when a module becomes confusing, redesign it.</p><p>Without equally fast architectural feedback, faster implementation creates a faster-growing legacy system. The promise of AI development is not more lines of code but moving human attention toward product intent, boundaries, tradeoffs, and judgment. Great architecture makes the correct next action locally discoverable and incorrect actions cheap to reject.</p><h2>As models improve, the delegation ceiling rises</h2><p>As models improve, the same explicit modules and seams let agents safely own progressively larger units of work. Today that may be a bounded implementation task; over time it can grow to designing a module, coordinating changes across modules, evolving contracts, or planning migrations. The structure does not need to loosen as model capability rises.</p><p>Humans can also grant agents more architectural authority over time. Agents may propose boundaries and dependencies while the harness verifies mechanical rules and sensitive changes still require review. Trust expands by scope instead of becoming unrestricted.</p><p>This architecture therefore does not depend on the limitations of today&#8217;s models. Better models raise the ceiling of work that can be delegated, while explicit ownership, contracts, and verification keep ambiguity and blast radius bounded.</p><h1>Appendix</h1><p>The appendix turns the preceding patterns into concrete repository shapes. It includes a complete generic tree and a small online-store example showing how core and domain modules divide responsibility.</p><h2>Complete repository tree</h2><p>This generic tree shows the complete repository shape without tying it to a particular product domain.</p><pre><code><code>repo/
&#9500;&#9472;&#9472; apps/
&#9474;   &#9500;&#9472;&#9472; app1/
&#9474;   &#9474;   &#9500;&#9472;&#9472; app/
&#9474;   &#9474;   &#9474;   &#9500;&#9472;&#9472; core/
&#9474;   &#9474;   &#9474;   &#9474;   &#9500;&#9472;&#9472; module1/
&#9474;   &#9474;   &#9474;   &#9474;   &#9474;   &#9500;&#9472;&#9472; docs/
&#9474;   &#9474;   &#9474;   &#9474;   &#9474;   &#9492;&#9472;&#9472; test/
&#9474;   &#9474;   &#9474;   &#9474;   &#9492;&#9472;&#9472; moduleN/
&#9474;   &#9474;   &#9474;   &#9492;&#9472;&#9472; domain/
&#9474;   &#9474;   &#9474;       &#9500;&#9472;&#9472; module1/
&#9474;   &#9474;   &#9474;       &#9474;   &#9500;&#9472;&#9472; docs/
&#9474;   &#9474;   &#9474;       &#9474;   &#9492;&#9472;&#9472; test/
&#9474;   &#9474;   &#9474;       &#9492;&#9472;&#9472; moduleN/
&#9474;   &#9474;   &#9500;&#9472;&#9472; bin/
&#9474;   &#9474;   &#9500;&#9472;&#9472; db_migrations/
&#9474;   &#9474;   &#9500;&#9472;&#9472; docs/
&#9474;   &#9474;   &#9500;&#9472;&#9472; harness/
&#9474;   &#9474;   &#9500;&#9472;&#9472; contracts/
&#9474;   &#9474;   &#9492;&#9472;&#9472; test_support/
&#9474;   &#9474;       &#9500;&#9472;&#9472; docs/
&#9474;   &#9474;       &#9492;&#9472;&#9472; test/
&#9474;   &#9500;&#9472;&#9472; app2/
&#9474;   &#9492;&#9472;&#9472; app3/
&#9500;&#9472;&#9472; shared/
&#9474;   &#9500;&#9472;&#9472; language1/
&#9474;   &#9474;   &#9500;&#9472;&#9472; bin/
&#9474;   &#9474;   &#9500;&#9472;&#9472; docs/
&#9474;   &#9474;   &#9500;&#9472;&#9472; harness/
&#9474;   &#9474;   &#9492;&#9472;&#9472; shared/
&#9474;   &#9474;       &#9492;&#9472;&#9472; core/
&#9474;   &#9474;           &#9500;&#9472;&#9472; module1/
&#9474;   &#9474;           &#9474;   &#9500;&#9472;&#9472; docs/
&#9474;   &#9474;           &#9474;   &#9492;&#9472;&#9472; test/
&#9474;   &#9474;           &#9492;&#9472;&#9472; moduleN/
&#9474;   &#9492;&#9472;&#9472; language2/
&#9500;&#9472;&#9472; e2e/
&#9474;   &#9500;&#9472;&#9472; bin/
&#9474;   &#9500;&#9472;&#9472; docs/
&#9474;   &#9500;&#9472;&#9472; tests/
&#9474;   &#9492;&#9472;&#9472; test_services/
&#9474;       &#9500;&#9472;&#9472; service1/
&#9474;       &#9474;   &#9500;&#9472;&#9472; docs/
&#9474;       &#9474;   &#9492;&#9472;&#9472; test/
&#9474;       &#9492;&#9472;&#9472; serviceN/
&#9500;&#9472;&#9472; bin/
&#9500;&#9472;&#9472; docker/
&#9500;&#9472;&#9472; docs/
&#9500;&#9472;&#9472; harness/
&#9500;&#9472;&#9472; AGENTS.md
&#9492;&#9472;&#9472; README.md
</code></code></pre><h2>Example modules</h2><p>Here is how a small online store might apply the module structure:</p><pre><code><code>apps/store/app/
&#9500;&#9472;&#9472; core/
&#9474;   &#9500;&#9472;&#9472; auth/
&#9474;   &#9500;&#9472;&#9472; observability/
&#9474;   &#9500;&#9472;&#9472; database/
&#9474;   &#9492;&#9472;&#9472; web/
&#9492;&#9472;&#9472; domain/
    &#9500;&#9472;&#9472; catalog/
    &#9500;&#9472;&#9472; orders/
    &#9492;&#9472;&#9472; returns/
</code></code></pre><p><strong>Core modules</strong></p><ul><li><p><code>core/auth</code> owns authentication and exposes login mechanics once for the entire app.</p></li><li><p><code>core/observability</code> attaches baseline telemetry at module seams so domain code does not recreate tracing and logging.</p></li><li><p><code>core/database</code> owns connections, transactions, and migration mechanics without owning domain tables.</p></li><li><p><code>core/web</code> owns HTTP server setup, shared middleware, request and response mechanics, and module route registration. Domain modules still own their uniquely prefixed endpoints.</p></li></ul><p><strong>Domain modules</strong></p><ul><li><p><code>domain/catalog</code> owns products, their descriptive information, tables, public operations, and <code>/catalog</code> endpoints.</p></li><li><p><code>domain/orders</code> owns the order lifecycle, order tables, public operations, and <code>/orders</code> endpoints.</p></li><li><p><code>domain/returns</code> owns return eligibility and workflows and publishes endpoints under <code>/returns</code>. It uses the orders module&#8217;s public interface rather than querying order tables.</p></li></ul>]]></content:encoded></item><item><title><![CDATA[A Blueprint for an Ideal Software Factory]]></title><description><![CDATA[A practical blueprint for AI software factories: spec-first pipelines, durable artifacts, human-in-the-loop gates, and portable context for scalable AI-driven software development.]]></description><link>https://jackkora.com/p/a-blueprint-for-an-ideal-software-factory</link><guid isPermaLink="false">https://jackkora.com/p/a-blueprint-for-an-ideal-software-factory</guid><dc:creator><![CDATA[Jack Kora]]></dc:creator><pubDate>Sat, 25 Jul 2026 21:06:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!qZCW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf435ef1-3376-4bb8-9540-bb19fdd0269c_1280x701.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qZCW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf435ef1-3376-4bb8-9540-bb19fdd0269c_1280x701.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qZCW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf435ef1-3376-4bb8-9540-bb19fdd0269c_1280x701.jpeg 424w, https://substackcdn.com/image/fetch/$s_!qZCW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf435ef1-3376-4bb8-9540-bb19fdd0269c_1280x701.jpeg 848w, https://substackcdn.com/image/fetch/$s_!qZCW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf435ef1-3376-4bb8-9540-bb19fdd0269c_1280x701.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!qZCW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf435ef1-3376-4bb8-9540-bb19fdd0269c_1280x701.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qZCW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf435ef1-3376-4bb8-9540-bb19fdd0269c_1280x701.jpeg" width="1280" height="701" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/df435ef1-3376-4bb8-9540-bb19fdd0269c_1280x701.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:701,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:577492,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://jackkora.com/i/208493104?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf435ef1-3376-4bb8-9540-bb19fdd0269c_1280x701.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qZCW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf435ef1-3376-4bb8-9540-bb19fdd0269c_1280x701.jpeg 424w, https://substackcdn.com/image/fetch/$s_!qZCW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf435ef1-3376-4bb8-9540-bb19fdd0269c_1280x701.jpeg 848w, https://substackcdn.com/image/fetch/$s_!qZCW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf435ef1-3376-4bb8-9540-bb19fdd0269c_1280x701.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!qZCW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf435ef1-3376-4bb8-9540-bb19fdd0269c_1280x701.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>In my recent posts I laid out the <a href="https://jackkora.com/p/a-framework-for-ai-dev-tooling-models">framework for thinking about software factories</a> and <a href="https://jackkora.com/p/mapping-the-ai-coding-orchestrator">reviewed sixteen of them</a>. I ended up with a question: what should an ideal software factory look like? And will it scale for the next 3-5 years?</p><p>My opinion is based on research and on my experience building a small internal software factory. That is where my conviction comes from, but it is likely also the source of some bias.</p><p>Please note that <strong>software factory</strong> is a newly emerging term for this product category. It&#8217;s not universally adopted across the industry, but I like it and I&#8217;ll use it instead of my previous orchestrator term.</p><h1>The developer flow</h1><p>I want to start with the developer experience that a software factory should enable.</p><p>It starts in the inner loop, where a developer shapes the requirements, spec, and architecture with a local agent. Once that is solid, the factory takes over: planning, implementation, audits, and the PR at the end. Then the work often returns to the inner loop for small fixes or final polish&#8212;typically on larger or UI-heavy tickets (small tickets can merge right in).</p><p>However, not every task starts locally. A bug may arrive from Slack, Jira/Linear, or an alert. Some factory tasks come from recurring jobs (eg, check observability for errors, look for dependencies to upgrade, etc).</p><p>Every ticket ends in one of two ways. If the factory clears its audits and gates, it opens a PR&#8212;or merges it when policy allows. If it is uncertain, needs missing input, or wants to change protected code, it pauses and contacts the developer through Slack, email, or the factory UI with the exact question or approval it needs. Once the developer responds, the run resumes from the same state.</p><h1>Capabilities a factory must support</h1><p>Here is a practical breakdown of the core capabilities.</p><p><strong>Take work from anywhere.</strong> A local spec, a Slack thread, Jira/Linear, or a monitor firing from logs and traces. Whatever the source, the factory converts the raw input into the first formal requirements artifact before the pipeline proceeds.</p><p><strong>Run recurring work.</strong> Security drift, performance regressions, user-feedback triage, etc are just some examples of workflows triggered by a schedule instead of a ticket.</p><p><strong>Keep the full task context together.</strong> Requirements, plans, review findings, and human decisions should be readable in one place and loadable into the inner loop.</p><p><strong>Make execution explicit and durable.</strong> Stages, state, gates, retries, and resumptions should be visible. The LLM work is non-deterministic, but the workflow around it should not be mysterious.</p><p><strong>Escalate by risk and uncertainty.</strong> Protected code, low confidence, incomplete requirements, and custom policies can all justify human-in-the-loop (HITL) intervention.</p><p><strong>Make disagreement cheap.</strong> When a developer rejects an LLM architecture choice or review finding, the fix should be an quick human-factory exchange followed by the factory resuming the ticket run.</p><p><strong>Send work backward.</strong> If implementation exposes a requirements gap, the factory should return the task upstream with a reason, repair the artifact, and continue from that stage.</p><p><strong>Learn from history.</strong> Dismissed findings, disputed decisions, and stalled runs should produce concrete suggestions for improving skills and repository instructions: &#8220;this review skill over-produces findings here,&#8221; or &#8220;the repository guidance no longer matches the architecture.&#8221;</p><p><strong>Move cleanly between inner and outer loops.</strong> A developer should be able to hand work off and pull it back without rebuilding context.</p><p>Together, these requirements point toward a spec-first pipeline: durable artifacts crossing explicit boundaries, with configurable HITL between them. Here&#8217;s what that architecture looks like.</p><h1>The architecture</h1><h2>Linear by default, limited DAG when justified</h2><p>Mature factories are converging on explicit pipeline stages with LLM calls inside them. Not one agent wandering freely from intent to PR.</p><p>Most of these pipelines should be linear. Requirements feed architecture, architecture feeds a plan, and a plan feeds implementation. A general-purpose DAG adds authoring and debugging complexity that most teams will not use. Specialized software should be opinionated; exposing every topology primitive pushes complexity onto the adopter.</p><p>That said, a sophisticated campaign loop (running multi-week projects) can call for a limited DAG:</p><ul><li><p><strong>Fan-out and fan-in:</strong> test several root-cause hypotheses or architecture options, or apply the same migration pipeline across many files.</p></li><li><p><strong>Work dependencies:</strong> sequence related workstreams inside a large project plan. This is typically more of a tree where tasks depend on each other.</p></li></ul><p>The factory should support those cases without turning into Airflow.</p><h2>Spec-first pipeline stages</h2><p>The factory should map the team&#8217;s spec-first workflow directly into pipeline stages, with each spec stage becoming one pipeline stage.</p><p>Any sensible spec-first flow can work. I use this one and will use it as a reference for this post.</p><p><code>requirements &#8594; architecture &#8594; plan &#8594; implement &#8594; code review</code></p><p>Each stage produces a durable artifact for the next. Spec Kit uses four stages, Kiro three, Microsoft seven. The exact count matters less than the contract between them.</p><p>The spec guides the run. After merge, the code becomes canonical and the spec becomes a historical record of how the code got there.</p><h2>Inside a stage: audit, fix, escalate</h2><p>However, stages are a bit more sophisticated than they seem. Every stage can be configured to run a loop - run the skill (plan, implement, etc), audit the output, fix what the agent can resolve confidently, and escalate the rest.</p><p>Complex artifacts may deserve several reviewers or audit passes, just as a design document may go through several human reviews. The factory should make the number of passes, prompts, roles, and models configurable.</p><p>HITL should support four forms:</p><ul><li><p><strong>Uncertainty-driven:</strong> low confidence or incomplete requirements.</p></li><li><p><strong>Fixed gate:</strong> a stage always pauses for review.</p></li><li><p><strong>Hook-driven:</strong> adopter-written checks can block progress.</p></li><li><p><strong>Declarative policy:</strong> paths or owners require approval.</p></li></ul><p>Declarative protected-code rules are especially useful in structured repositories. They are similar to GitHub&#8217;s CODEOWNERS. They can be implemented through hooks, but I want to bring them to the forefront because they are a practical way to manage risk. For example, authentication, security, and database migrations may stay human-gated indefinitely while other paths are auto-merged.</p><p>Escalation can also send work backward across stages. If implementation finds a requirements gap, it returns the task to requirements with an explanation. The artifact is revised and the pipeline runs forward again through the same gates. That is better than improvising or failing the run, and because the transition is explicit, it is fully auditable.</p><h2>Stage contract: formal artifacts only</h2><p>So how do the stages communicate with each other? Only the completed artifact&#8212;whether a document, code change, branch, or PR&#8212;crosses a stage boundary. Not the full conversation, partial drafts, or failed audit attempts.</p><p>That keeps context bounded because intermittent state is discarded between stages. It also makes inner/outer-loop handoff portable, allows the pipeline to resume from the last completed stage, and gives humans something readable and diffable. A person can reject one artifact without replaying the entire conversation that produced it.</p><p>Spec Kit&#8217;s <a href="http://constitution.md">constitution.md</a> &#8594; <a href="http://plan.md">plan.md</a> &#8594; <a href="http://tasks.md">tasks.md</a> and Kiro&#8217;s <a href="http://requirements.md">requirements.md</a> &#8594; <a href="http://design.md">design.md</a> &#8594; <a href="http://tasks.md">tasks.md</a> are file-level versions of the same contract.</p><h2>Skills implement stages</h2><p>A stage typically contains at least two skills: a main skill that creates the artifact and a review skill that audits it. In the plan stage, those might be create plan and review plan. In the implementation stage, they are implement and code review.</p><p>The artifact can be a document, code change, branch, or PR. The rule is the same: the main skill produces it, the review skill evaluates it, and the stage emits one completed artifact across the boundary.</p><p>Inside those skills, anything goes: parallel agents, adversarial verification, multi-model consensus, or deterministic post-processing. A code review skill, for example, might run parallel security, quality, and architecture reviewers, followed by an adversarial pass and synthesis. Nine agents inside, one findings artifact outside. Stage-internal complexity is normal; it just should not leak into the outer workflow unnecessarily.</p><p>This also means that a developer can run the same pipeline locally by invoking each skill manually.</p><h2>The factory owns its ticket</h2><p>Every task gets a factory ticket, regardless of where it came from. Requirements, plans, findings, audit entries, and HITL decisions attach to it.</p><p>Many valid tasks&#8212;the Slack bug, an alert, an ad-hoc cleanup&#8212;have no external ticket and should not be forced to create one. When an external Jira or Linear ticket does exist, the factory treats it as input and uses its context to produce stage artifacts on the factory ticket. This avoids mirroring every intermediate artifact back into the external system.</p><p>The factory should expose ticket context through MCP, an API, or its own CLI. The mechanism is secondary. The requirement is that a developer&#8212;or another agent&#8212;can load the complete task outside the factory.</p><p>The PR is the other important artifact. CI failures return the task to implementation, PR comments become new factory input, and merges remain human-controlled where required.</p><p>This design produces the audit trail almost automatically: every artifact, approval, dismissal, and timestamp is attached to the same ticket.</p><h1>A proprietary harness is fine. A closed one isn&#8217;t.</h1><p>I started this research convinced the factory had to run the same agent harness developers use locally (eg, claude code, codex, etc). I was wrong.</p><p>A proprietary harness may be better in the outer loop. The vendor can optimize context retrieval, model routing, retries, audit, and long-running execution as one system. The problem is not a proprietary harness. It is a closed one.</p><p>The minimum bar is portable skills and artifacts, and complete context accessible outside the product. A developer must be able to write a spec in the inner loop, hand it to the factory, then pull back the branch and every relevant decision. Using Claude Code or Codex is useful when available, but not mandatory.</p><p>Price changed my view too. I expected proprietary orchestration to carry a huge premium. From the public pricing I could find, a factory with BYOK lands near raw agent costs at team scale&#8212;not an order of magnitude above them.</p><h1>Why this scales as models improve</h1><p>This is the million dollar question. Will this scale as LLMs get better and better? Or will advanced LLMs make this scaffolding obsolete?</p><p>I believe that this will scale as models get better. Model-dependent pieces remain replaceable: models themselves, skills, and state. The pipeline does not encode or rely on any specific model behavior.</p><p>The pipeline encodes organizational thinking: trust boundaries, audit requirements, ownership, and acceptable blast radius. Better models do not change those.</p><p>However, better models will produce better output, handle larger tasks, and move through the same architecture with fewer audit failures, escalations, and send-backs.</p><p>Even a perfect implementation model does not remove the need to approve a high-stakes spec, retain an audit trail, or control who merges protected code. Those requirements come from an accountability framework, not model capability.</p><p>So with better LLM model, this will scale by being able to tackle larger tasks with higher quality.</p><div><hr></div><p>I don&#8217;t expect every factory to look exactly like this, but I do think these are the factory aspects that teams should consider.</p><p>The software factory market is still nascent. Even the term itself is not universal, and the products on the market are quite different from one another. I hope this gives you a concrete way to evaluate them and choose the best fit for your needs.</p>]]></content:encoded></item><item><title><![CDATA[Mapping the AI coding orchestrator (software factory) market]]></title><description><![CDATA[After this post went live I&#8217;ve learned that there&#8217;s a new term emerging - Software Factories.]]></description><link>https://jackkora.com/p/mapping-the-ai-coding-orchestrator</link><guid isPermaLink="false">https://jackkora.com/p/mapping-the-ai-coding-orchestrator</guid><dc:creator><![CDATA[Jack Kora]]></dc:creator><pubDate>Mon, 29 Jun 2026 18:01:39 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!qqBc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d31b67-ce1e-4d8b-ad28-e517b9e22c74_1024x572.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qqBc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d31b67-ce1e-4d8b-ad28-e517b9e22c74_1024x572.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qqBc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d31b67-ce1e-4d8b-ad28-e517b9e22c74_1024x572.png 424w, https://substackcdn.com/image/fetch/$s_!qqBc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d31b67-ce1e-4d8b-ad28-e517b9e22c74_1024x572.png 848w, https://substackcdn.com/image/fetch/$s_!qqBc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d31b67-ce1e-4d8b-ad28-e517b9e22c74_1024x572.png 1272w, https://substackcdn.com/image/fetch/$s_!qqBc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d31b67-ce1e-4d8b-ad28-e517b9e22c74_1024x572.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qqBc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d31b67-ce1e-4d8b-ad28-e517b9e22c74_1024x572.png" width="1024" height="572" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c4d31b67-ce1e-4d8b-ad28-e517b9e22c74_1024x572.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:572,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:233073,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://jackkora.com/i/203493505?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d31b67-ce1e-4d8b-ad28-e517b9e22c74_1024x572.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qqBc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d31b67-ce1e-4d8b-ad28-e517b9e22c74_1024x572.png 424w, https://substackcdn.com/image/fetch/$s_!qqBc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d31b67-ce1e-4d8b-ad28-e517b9e22c74_1024x572.png 848w, https://substackcdn.com/image/fetch/$s_!qqBc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d31b67-ce1e-4d8b-ad28-e517b9e22c74_1024x572.png 1272w, https://substackcdn.com/image/fetch/$s_!qqBc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d31b67-ce1e-4d8b-ad28-e517b9e22c74_1024x572.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><blockquote><p>After this post went live I&#8217;ve learned that there&#8217;s a new term emerging - Software Factories. I like this term - it&#8217;s exactly what I meant by coding orchestrator.</p></blockquote><p>At some point in every org&#8217;s AI adoption curve, gaining further effectiveness requires a cloud-based coding-agent orchestrator. The work that benefits is the kind that should run without a developer driving &#8212; &#8220;fix this bug&#8221;, &#8220;investigate this log spike&#8221;, or &#8220;ship this feature I&#8217;ve already specced&#8221;. Solo developers benefit too. But the inherent standardization that an orchestrator brings is more beneficial for larger orgs.</p><p>In this post I look at how Stripe, Coinbase, Uber, Meta, and Shopify built their internal AI coding tools. I also look at how they compare to on-the-market AI coding tools. I was surprised to find out that there&#8217;s a clear division between internal vs. on-the-market tools.</p><p>The technical language used in this article &#8212; models vs agent harnesses vs repo harnesses, inner vs outer loops &#8212; is something I laid out in a <a href="https://jackkora.com/p/a-framework-for-ai-dev-tooling-models">previous post</a>. This post is a survey of sixteen orchestrators (all that I could find) analyzed along these axis.</p><h1>What counts as an orchestrator</h1><p>There are several categories that got swept up in my initial broad search. These are all excluded from my analysis:</p><ul><li><p><strong>Coding agents</strong> (Claude Code, Codex CLI) &#8212; also called the agent harness. They aren&#8217;t orchestrators themselves.</p></li><li><p><strong>Point-solution tools</strong> offer a single specialized capability &#8212; code-review tools (CodeRabbit, Greptile), bug detectors (Cursor Bugbot). One scope, one job.</p></li></ul><p>What&#8217;s left is the layer that takes an intent &#8212; &#8220;fix this bug&#8221;, &#8220;investigate this log spike&#8221;, or &#8220;ship this feature I&#8217;ve already specced&#8221; &#8212; and runs it through one or more coding agents to completion. Sixteen of them have public details good enough to analyze.</p><ul><li><p><strong>On-the-market (8):</strong> Conductor, Sculptor, Nimbalyst, OpenHands, Warp Oz, Augment Code, Factory Droid, Devin Desktop.</p></li><li><p><strong>Internal (8):</strong> Stripe Minions, Block Builderbot, Coinbase Mux + NodeSmith, Shopify Roast, Uber LangFX, Meta Confucius, Google&#8217;s migration pipeline, Airbnb&#8217;s batch migration pipeline.</p></li></ul><h1>Findings, by axis</h1><h2>Major findings</h2><p>Two patterns dominate the dataset - the workflow model and the agent harness support.</p><h3>Two workflow models</h3><p>All orchestrators break into two workflow models:</p><ul><li><p><strong>Handoff orchestrators.</strong> Non-deterministic. The orchestrator hands work off to the LLM via a single prompt or skill; the LLM controls 100% of the flow from there on.</p></li><li><p><strong>Pipeline orchestrators.</strong> Deterministic. The devs design the pipeline as explicit steps, interleaving calls to a coding agent and HITL (Human-In-The-Loop) gates between deterministic execution. The orchestrator runs the pipeline as a state machine.</p></li></ul><p>Here&#8217;s the interesting part. The handoff / pipeline split is almost perfectly the build/buy split.</p><p><strong>Internal platforms cluster on the pipeline side.</strong> Stripe Minions, Shopify Roast, Uber LangFX, Meta Confucius, Coinbase NodeSmith, Google&#8217;s migration pipeline, Airbnb&#8217;s migration pipeline &#8212; all of them. Stripe&#8217;s Blueprints alternate deterministic and agentic nodes; Shopify&#8217;s Roast is a Ruby DSL with named cogs; Meta&#8217;s Confucius is a DAG with HITL gates between stages. The pipeline languages vary &#8212; Ruby (Roast), LangGraph/Python (LangFX), bespoke DSL (Minions), state-machine-in-code (Airbnb) &#8212; but the shape is uniform: deterministic steps with LLM and HITL nodes interleaved. The rest fit the same shape &#8212; see appendix for the per-tool teardown. Block&#8217;s Builderbot and Coinbase&#8217;s Mux look like handoff at first glance but sit on top of internal pipeline infrastructure; even where internal orgs ship handoff dispatch, what&#8217;s underneath is pipeline.</p><p><strong>On-the-market orchestrators cluster on the handoff side.</strong> Conductor, Sculptor, Nimbalyst, Warp Oz, OpenHands, Devin Desktop. Each dispatches an agent and lets it figure out the steps.</p><p><strong>Two on-the-market products break the handoff pattern.</strong> Factory Droid ships Custom Droids you author; Augment Code ships a coordinator + specialist + verifier architecture with explicit scope-approval HITL. Both target enterprise structured workloads. Both expose real pipeline design.</p><h3>Agent harnesses</h3><p>How does the orchestrator relate to the coding agent it dispatches? Three patterns:</p><ul><li><p><strong>Proprietary agent harness.</strong> The orchestrator ships its own. Block built Goose, Meta built Confucius, Stripe forked Goose into Minions. On-the-market, Factory Droid runs Droid and Augment Code runs Auggie underneath. The org owns and maintains its own agent harness and uses raw LLM models from LLM vendors.</p></li><li><p><strong>BYO direct.</strong> The orchestrator dispatches a third-party agent harness untouched &#8212; native execution, no interception. Internally, Airbnb runs Claude Code as-is and Coinbase mixes harnesses through Mux. On-the-market, Conductor, Sculptor, Nimbalyst, OpenHands, and Devin Desktop all pass the chosen harness through; <a href="https://agentclientprotocol.com">ACP (the Agent Coordination Protocol)</a> is the standard underpinning the latter two.</p></li><li><p><strong>BYO wrapped.</strong> The orchestrator runs the chosen agent harness inside its own context/memory layer. Warp Oz adds &#8220;Agent Memory&#8221; and &#8220;Skills&#8221; on top of execution; Augment Code layers a structured context including system prompts and workspace metadata. The wrapper can degrade the inner harness&#8217;s context &#8212; Augment Code&#8217;s own docs <a href="https://www.augmentcode.com/tools/intent-vs-claude-code">explicitly disclose</a> the added context. Verify before depending on it.</p></li></ul><p>Internally, six of the eight platforms in the dataset have consolidated on one agent harness across the org: Goose at Stripe and Block, Claude Code at Anthropic and Airbnb, Confucius at Meta, LangFX at Uber. Polyglot agent harnesses are rare inside.</p><p>On-the-market, the 8 products split into 5 BYO direct, 2 BYO wrapped, and 1 Proprietary (Factory Droid).</p><h2>Supporting patterns</h2><p>Smaller findings that reinforce or color the major split.</p><h3>HITL surfaces</h3><p>How do humans interact with the orchestrator? That&#8217;s mostly settled - Slack, Linear, etc. The chat-in-dashboard anti pattern is essentially dead.</p><p>When do humans interact with the orchestrator? That&#8217;s a deeper question with three approaches in the wild:</p><ul><li><p><strong>Confidence-driven.</strong> The agent grades itself, a verifier sub-agent grades (Meta KernelEvolve, Augment Code&#8217;s verifier role, Devin&#8217;s auto-review), or the user sets sensitivity dials (Cursor Bugbot effort levels).</p></li><li><p><strong>Single-gate.</strong> One HITL event, otherwise autonomous. Two variants worth distinguishing: upfront plan-approval (Cursor&#8217;s long-running agents, Augment Code scope approval, Devin&#8217;s preflight checklist) and exit-only PR review (Copilot Coding Agent, Stripe Minions, OpenHands headless). Upfront catches direction errors before compute spend; exit reuses existing review process.</p></li><li><p><strong>Hook-driven.</strong> The adopter writes scripts or config that fires at named execution milestones &#8212; pre-plan, pre-tool-call, post-tool-call, pre-commit. The script can halt the agent (<code>exit 1</code>), modify state, or run guardrails. OpenHands&#8217; <code>hooks.json</code> is the canonical declarative-registration version; Claude Code ships its own hook system. Hooks are the lowest-level, most flexible HITL surface &#8212; you write code and it runs.</p></li></ul><p>The three approaches aren&#8217;t equivalent in coverage. Confidence-driven is opaque to the adopter &#8212; you tune indirectly by enriching context, not by setting thresholds. Single-gate is binary &#8212; one ask, then nothing until the PR. Hook-driven is fully programmable but pushes the work onto the user and offers no built-in human-prompt UI.</p><h3>Loop types (and the new campaign loop)</h3><p>All sixteen orchestrators target outer-loop work &#8212; the developer hands off and checks back. Inner-vs-outer isn&#8217;t an interesting axis in this dataset; orchestrators exist for outer-loop work by definition. What IS interesting is a third loop the binary missed: the <strong>campaign loop</strong>.</p><p>Campaign-loop work is autonomous multi-day-to-multi-week execution against a goal. The developer doesn&#8217;t check back in any meaningful cadence; the orchestrator owns long-running state.</p><ul><li><p><strong>Uber Shepherd</strong> runs multi-day migration campaigns across services.</p></li><li><p><strong>Meta REA</strong> has a &#8220;hibernate-and-wake&#8221; pattern that bridges multi-day training waits inside one logical run.</p></li><li><p><strong>Factory Missions</strong> have a median around two hours, with 14% running over 24 hours and the longest recorded at 16 days.</p></li><li><p><strong>Google&#8217;s migration pipeline</strong> has run multi-month migrations across 149 teams.</p></li></ul><p>These aren&#8217;t outer-loop with a slow review cadence. The orchestrator is driving goal-with-stopping-criteria rather than a task-with-handoff. What makes the loop distinct is the durable state &#8212; these orchestrators persist work across host / service boundaries because no single machine outlives the campaign.</p><p>Most orgs aren&#8217;t close to needing campaign-loop tooling, so I won&#8217;t focus on it for the rest of this post.</p><h2>What else stood out</h2><ul><li><p><strong>Repo harness.</strong> Every orchestrator in the dataset respects the standard repo-harness files &#8212; <code>AGENTS.md</code>, <code>CLAUDE.md</code>, <code>.cursorrules</code>, and friends all coexist. No tool earns the &#8220;imposes&#8221; verdict.</p></li><li><p><strong>Audience.</strong> Individual-developer tools (Conductor, Sculptor, Nimbalyst, Warp Oz, Devin Desktop) all sit on the handoff side. Org-wide tools (Factory Droid, Augment Code, all internal platforms) all sit on the pipeline side.</p></li><li><p><strong>BYO harness orchestrators are a real 2026 category.</strong> Most on-the-market products now let the user swap agent harnesses. ACP and broad MCP adoption made the swap technically straightforward by mid-2026 &#8212; with the caveat that wrapping can silently degrade the inner harness.</p></li></ul><h1>The synthesis</h1><ul><li><p>The market is bifurcated along the build/buy boundary. <strong>On-the-market:</strong> handoff dispatch with BYO harnesses (7 of 8 products). <strong>Internal:</strong> pipelines with Proprietary harnesses (6 of 8 platforms).</p></li><li><p><strong>Internal engineering teams are using this framework&#8217;s vocabulary.</strong> Coinbase explicitly segments inner loop vs. outer loop in its own engineering docs. Anthropic and Shopify use the same distinction internally. Three independent organizations adopted this mental model before <a href="https://jackkora.com/p/a-framework-for-ai-dev-tooling-models">my framework post</a> was published.</p></li><li><p><strong>Factory Droid &amp; Augment Code are the only on-the-market pipeline orchestrators.</strong> But neither supports Claude Code or Codex CLI. Factory Droid runs Droid &#8212; its own agent harness (you can use any LLM model). Augment Code runs Auggie underneath, also Augment&#8217;s own agent harness with its own model routing.</p></li></ul><h1>This leaves us with some questions&#8230;</h1><p>The insights from this research left me with some new questions:</p><ul><li><p><strong>Why does the handoff/pipeline bifurcation track the build/buy boundary so closely?</strong> Is it just headcount &#8212; bigger orgs build internal platforms &#8212; or is something else pulling the architectures apart?</p></li><li><p><strong>Similarly is Proprietary agent harness vs. BYO here to stay?</strong> Are Proprietary harnesses beneficial? Will they survive?</p></li><li><p><strong>What would be an ideal orchestrator that will scale for the next 3-5 years?</strong> I don&#8217;t think any of the on-the-market orchestrators are it. So what would it take?</p></li></ul><p>More on this in a follow up soon!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://jackkora.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h1>Addendum</h1><p>The matrix and vendor blurbs below is the raw data behind the synthesis. And all tools are linked to their primary source.</p><p><strong>How to read the matrix</strong></p><ul><li><p><strong>Build/buy</strong> &#8212; On-the-market (commercial or OSS) product vs. internal platform. The build/buy split is the central organizing distinction in the post.</p></li><li><p><strong>Workflow model</strong> &#8212; <em>Handoff</em> = one prompt, agent controls the flow. <em>Pipeline</em> = explicit deterministic + LLM steps wired by the adopter.</p></li><li><p><strong>Agent harness</strong> &#8212; <em>Proprietary</em> = built and owned by the org/vendor. <em>BYO direct</em> = third-party harness run as-is. <em>BYO wrapped</em> = third-party harness inside a vendor context/memory layer.</p></li><li><p><strong>Model</strong> &#8212; <em>Open</em> = any LLM, BYOK. <em>Fixed</em> = vendor picks.</p></li><li><p><strong>HITL</strong> &#8212; Three approaches from the post. <em>Confidence</em> = agent grades itself. <em>Single-gate</em> = one HITL event, otherwise autonomous; variants noted as <em>(upfront)</em> plan-approval or <em>(exit)</em> PR review. <em>Hook</em> = adopter-authored scripts at named milestones.</p></li><li><p><strong>Audience</strong> &#8212; <em>Individual</em> = solo dev installs. <em>Org</em> = deployed as a platform. <em>Both</em> = packaged for either.</p></li></ul><p><strong>The matrix</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4rTs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8ec6cc8-c765-4d8c-9471-454dcad54d38_2668x1945.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4rTs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8ec6cc8-c765-4d8c-9471-454dcad54d38_2668x1945.png 424w, https://substackcdn.com/image/fetch/$s_!4rTs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8ec6cc8-c765-4d8c-9471-454dcad54d38_2668x1945.png 848w, https://substackcdn.com/image/fetch/$s_!4rTs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8ec6cc8-c765-4d8c-9471-454dcad54d38_2668x1945.png 1272w, https://substackcdn.com/image/fetch/$s_!4rTs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8ec6cc8-c765-4d8c-9471-454dcad54d38_2668x1945.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4rTs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8ec6cc8-c765-4d8c-9471-454dcad54d38_2668x1945.png" width="1456" height="1061" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e8ec6cc8-c765-4d8c-9471-454dcad54d38_2668x1945.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1061,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:595198,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://jackkora.com/i/203493505?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8ec6cc8-c765-4d8c-9471-454dcad54d38_2668x1945.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4rTs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8ec6cc8-c765-4d8c-9471-454dcad54d38_2668x1945.png 424w, https://substackcdn.com/image/fetch/$s_!4rTs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8ec6cc8-c765-4d8c-9471-454dcad54d38_2668x1945.png 848w, https://substackcdn.com/image/fetch/$s_!4rTs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8ec6cc8-c765-4d8c-9471-454dcad54d38_2668x1945.png 1272w, https://substackcdn.com/image/fetch/$s_!4rTs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8ec6cc8-c765-4d8c-9471-454dcad54d38_2668x1945.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>On-the-market tools</h2><h3><a href="https://www.conductor.build">Conductor</a> (commercial)</h3><p>Free macOS desktop app (Apple Silicon only) from a YC S24 company that raised a $22M Series A. Spawns parallel Claude Code / Codex CLI / Cursor sessions in isolated git worktrees, each with its own branch, terminal, and Diff Viewer. Adds no agent logic of its own &#8212; thin orchestration shell over the underlying harnesses; users bring their own subscriptions. The canonical &#8220;outer loop defined by attention, not execution&#8221; example.</p><ul><li><p><a href="http://conductor.build">conductor.build</a></p></li><li><p><a href="http://conductor.build/docs/concepts/workflow">conductor.build/docs/concepts/workflow</a></p></li></ul><h3><a href="https://imbue.com/sculptor">Sculptor</a> (OSS)</h3><p>Open-source Mac/Linux desktop app (Electron) from Imbue. Wraps Claude Code and Codex in Docker containers (vs Conductor&#8217;s worktrees) for tighter isolation, with a Pairing Mode that bidirectionally syncs container files into the developer&#8217;s IDE for live co-editing. Explicitly labeled &#8220;experimental research preview&#8221;; MCP and Sculptor-layer <code>CLAUDE.md</code> handling still roadmap.</p><ul><li><p><a href="http://imbue.com/sculptor">imbue.com/sculptor</a></p></li><li><p><a href="http://github.com/imbue-ai/sculptor">github.com/imbue-ai/sculptor</a></p></li></ul><h3><a href="https://nimbalyst.com">Nimbalyst</a> (OSS)</h3><p>Stravu&#8217;s MIT-licensed successor to Crystal (deprecated Feb 2026). Cross-platform desktop (Mac/Win/Linux + iOS companion) for parallel Claude Code / Codex / OpenCode / Aider sessions in isolated worktrees. Adds a session kanban, inline red/green diff review, and an extension SDK with ACP alpha for Copilot. Very young (open-sourced April 2026); team tier not yet GA.</p><ul><li><p><a href="http://nimbalyst.com">nimbalyst.com</a></p></li><li><p><a href="http://github.com/nimbalyst/nimbalyst">github.com/nimbalyst/nimbalyst</a></p></li></ul><h3><a href="https://www.openhands.dev">OpenHands</a> (OSS)</h3><p>All Hands AI&#8217;s MIT-licensed agent platform, 78K+ GitHub stars. Docker-sandboxed CodeAct agent with LiteLLM model abstraction (100+ providers). Five surfaces: Agent Canvas GUI, headless CLI, GitHub Action triggered by <code>fix-me</code> labels, Slack, and ACP for IDE embedding. The cleanest external zero-chat outer-loop path; <code>.openhands/hooks.json</code> is the canonical declarative hook-driven HITL primitive in the dataset.</p><ul><li><p><a href="http://openhands.dev">openhands.dev</a></p></li><li><p><a href="http://docs.openhands.dev/openhands/usage/run-openhands/github-action">docs.openhands.dev/openhands/usage/run-openhands/github-action</a></p></li><li><p><a href="http://docs.openhands.dev/openhands/usage/customization/repository">docs.openhands.dev/openhands/usage/customization/repository</a></p></li></ul><h3><a href="https://www.warp.dev/cloud">Warp Oz</a> (commercial)</h3><p>The first multi-harness cloud-agent control plane: runs Warp Agent, Claude Code, and Codex side-by-side under unified governance and credit billing. Triggers via Slack <code>@Oz</code>, Linear issue assignment, GitHub Actions, cron, or webhooks; HITL surfaces are native to the triggering tool. Internal stat: 60% of Warp&#8217;s own PRs by Oz agents. Warp terminal open-sourced May 2026.</p><ul><li><p><a href="http://warp.dev/cloud">warp.dev/cloud</a></p></li><li><p><a href="http://warp.dev/blog/multi-harness-cloud-agent-orchestration">warp.dev/blog/multi-harness-cloud-agent-orchestration</a></p></li><li><p><a href="http://warp.dev/blog/oz-orchestration-platform-cloud-agents">warp.dev/blog/oz-orchestration-platform-cloud-agents</a></p></li></ul><h3><a href="https://www.augmentcode.com">Augment Code</a> (commercial)</h3><p>Three-surface platform &#8212; Cosmos (IDE plugin), Auggie CLI, Intent (macOS desktop orchestrator) &#8212; anchored on a proprietary Context Engine indexing 400K+-file monorepos. Intent&#8217;s BYOA wraps Claude Code / Codex / OpenCode in a coordinator + specialist + verifier architecture with explicit scope-approval HITL. <strong>Warning:</strong> BYOA agents get <em>degraded</em> Context Engine access vs first-party Auggie, <a href="https://www.augmentcode.com/tools/intent-vs-claude-code">Augment&#8217;s own docs disclose</a> it.</p><ul><li><p><a href="http://augmentcode.com">augmentcode.com</a></p></li><li><p><a href="http://augmentcode.com/blog/intent-a-workspace-for-agent-orchestration">augmentcode.com/blog/intent-a-workspace-for-agent-orchestration</a></p></li><li><p><a href="http://docs.augmentcode.com/context-services/mcp/overview">docs.augmentcode.com/context-services/mcp/overview</a></p></li></ul><h3><a href="https://factory.ai">Factory Droid</a> (commercial)</h3><p>Multi-specialist outer-loop platform on a proprietary harness (Droid). A Delegator decomposes work and dispatches to Code / Review / Knowledge / Reliability / Product / Tutorial Droid sub-agents. Flagship &#8220;Missions&#8221; feature is the only external product with campaign-loop duration: median ~2hr, 14% over 24hr, longest recorded 16 days. $1.5B valuation April 2026; reads <code>AGENTS.md</code> only (Factory co-authored the standard with OpenAI).</p><ul><li><p><a href="http://factory.ai">factory.ai</a></p></li><li><p><a href="http://factory.ai/news/missions">factory.ai/news/missions</a></p></li><li><p><a href="http://docs.factory.ai/cli/configuration/agents-md">docs.factory.ai/cli/configuration/agents-md</a></p></li></ul><h3><a href="https://devin.ai">Devin Desktop</a> (commercial)</h3><p>Cognition&#8217;s June 2026 rebrand of the Windsurf acquisition. Agent Command Center kanban manages local + cloud + third-party (ACP-hosted Codex / Claude / Gemini / OpenCode / Junie) agents from one surface. The fleet-management piece of Cognition&#8217;s product line, separate from Devin Cloud (excluded as a coding agent, not an orchestrator). Devin 2.1&#8217;s confidence scoring (&#128994;&#128993;&#128308;) is the canonical confidence-driven HITL.</p><ul><li><p><a href="http://devin.ai">devin.ai</a></p></li><li><p><a href="http://cognition.com/blog/introducing-devin-desktop">cognition.com/blog/introducing-devin-desktop</a></p></li><li><p><a href="http://cognition.com/blog/devin-2-1">cognition.com/blog/devin-2-1</a></p></li></ul><h2>Internal</h2><h3>Stripe Minions</h3><p>The prime empirical example of the post&#8217;s thesis. Engineer tags a Slack bot &#8594; pre-warmed AWS devbox containing Stripe&#8217;s 30M-line monorepo spins up in under 10 seconds &#8594; Toolshed (Stripe&#8217;s ~500-tool MCP server) provides context &#8594; a Blueprint alternates deterministic steps (git, lint, CI) with agentic LLM nodes &#8594; PR opens, CI green, no human in the loop. <strong>1,300+ PRs/week merged.</strong> Built on a fork of Block&#8217;s Goose with interruptibility and confirmation prompts deliberately removed.</p><ul><li><p><a href="http://stripe.dev/blog/minions-stripes-one-shot-end-to-end-coding-agents">stripe.dev/blog/minions-stripes-one-shot-end-to-end-coding-agents</a></p></li><li><p><a href="http://stripe.dev/blog/minions-stripes-one-shot-end-to-end-coding-agents-part-2">stripe.dev/blog/minions-stripes-one-shot-end-to-end-coding-agents-part-2</a></p></li></ul><h3>Block Builderbot</h3><p>Block&#8217;s internal orchestration layer on Goose (which Block built and donated to the Linux Foundation Dec 2025). Vertically integrated stack: Anthropic Claude &#8594; Block Goose &#8594; Anthropic MCP &#8594; Block Builderbot. Slack-native @builderbot dispatch + Linear/Jira pickup; live thread updates while the agent works across the monorepo (Cash App, Square, all subsidiaries). <strong>~1,500 PRs/week merged, ~15% of all production code changes.</strong></p><ul><li><p><a href="http://block.xyz/inside/block-rolls-out-builderbot">block.xyz/inside/block-rolls-out-builderbot</a></p></li><li><p><a href="http://github.com/block/goose">github.com/block/goose</a></p></li><li><p><a href="http://github.com/block/builderbot">github.com/block/builderbot</a></p></li></ul><h3>Coinbase Mux + NodeSmith</h3><p>Coinbase explicitly segments inner vs outer loop in its own engineering docs &#8212; direct framework-vocabulary match. <strong>Forge</strong> is the Slack/Linear/GitHub-native task agent (1,000+ engineers, 5% of merged PRs, 150h&#8594;15h cycle time). <strong>Mux</strong> layers parallel-agent orchestration on Forge (600+ users, 5,068 merged PRs across 461 repos in one month). <strong>NodeSmith</strong> is a two-phase pipeline for blockchain node upgrades &#8212; LLM Triage Agent + Upgrade Orchestrator with 5 specialized sub-agents blending LLM reasoning with deterministic Python. OpenAI-compatible model router used daily by 1,500+ engineers; explicitly multi-vendor.</p><ul><li><p><a href="http://coinbase.com/blog/coding-had-a-concurrency-problem-how-mux-helped-solve-it">coinbase.com/blog/coding-had-a-concurrency-problem-how-mux-helped-solve-it</a></p></li><li><p><a href="http://coinbase.com/blog/NodeSmith-AI-Driven-Automation-for-Blockchain-Node-Upgrades">coinbase.com/blog/NodeSmith-AI-Driven-Automation-for-Blockchain-Node-Upgrades</a></p></li><li><p><a href="http://coinbase.com/blog/Tools-for-Developer-Productivity-at-Coinbase">coinbase.com/blog/Tools-for-Developer-Productivity-at-Coinbase</a></p></li></ul><h3>Shopify Roast</h3><p>Two-layer stack. <strong>Genie</strong> is an internal LLM proxy that sits under every AI tool Shopify uses (Claude Code, Copilot, Cursor, Codex) &#8212; PII masking, prompt-injection detection, cost analytics, model switching, plus <strong>24+ internal MCP servers</strong> exposing Vault, GSD, data warehouse, Salesforce, Slack, Google Workspace, GitHub, Figma. <strong>Roast</strong> is the Ruby DSL workflow framework on top: named cogs (<code>chat</code>, <code>agent</code>, <code>ruby</code>, <code>cmd</code>, <code>map</code>, <code>repeat</code>) interleave deterministic code with LLM calls. Open-sourced June 2025. Tobi L&#252;tke&#8217;s April 2025 memo: <em>&#8220;reflexive AI usage is now a baseline expectation.&#8221;</em></p><ul><li><p><a href="http://shopify.engineering/introducing-roast">shopify.engineering/introducing-roast</a></p></li><li><p><a href="http://github.com/Shopify/roast">github.com/Shopify/roast</a></p></li><li><p><a href="http://bvp.com/atlas/inside-shopifys-ai-first-engineering-playbook">bvp.com/atlas/inside-shopifys-ai-first-engineering-playbook</a></p></li></ul><h3>Uber LangFX</h3><p>Full-stack internal AI dev suite for ~5,000 engineers on a multi-hundred-million-line monorepo. Three layers: end-user tools (uReview, AutoCover, Minion, Shepherd, Validator), agent framework (LangFX &#8212; opinionated LangGraph wrapper integrated with Michelangelo), infra (AIFX CLI, MCP Gateway exposing 10,000+ internal services as MCP tools, Agent Builder). <strong>60K agent tasks/week. 92% of devs use agents monthly; 11% of PRs by agents.</strong> Multi-vendor model strategy benchmarked per task (Claude-4-Sonnet + o4-mini-high for uReview was optimal).</p><ul><li><p><a href="http://uber.com/blog/ureview/">uber.com/blog/ureview/</a></p></li><li><p><a href="http://newsletter.pragmaticengineer.com/p/how-uber-uses-ai-for-development">newsletter.pragmaticengineer.com/p/how-uber-uses-ai-for-development</a></p></li><li><p><a href="http://aaif.io/blog/how-uber-runs-60000-ai-agent-tasks-per-week-with-mcp/">aaif.io/blog/how-uber-runs-60000-ai-agent-tasks-per-week-with-mcp/</a></p></li></ul><h3>Meta Confucius</h3><p>Specialist-per-domain pattern on a shared internal harness (Confucius &#8212; ~2 years old, 60+ applications, DAG workflows + HITL gates). <strong>REA</strong> owns the ML experimentation loop for ads ranking (hibernate-and-wake across multi-day training waits). <strong>KernelEvolve</strong> owns GPU/MTIA/AMD kernel optimization (60%+ inference throughput on Andromeda; ISCA 2026 paper). <strong>Tribal Knowledge Agents</strong> produce ~59 concise context files per codebase (40% fewer tool calls per task downstream). <strong>Capacity Efficiency</strong> owns perf regression detection + fix generation.</p><ul><li><p><a href="http://engineering.fb.com/2026/03/17/developer-tools/ranking-engineer-agent-rea">engineering.fb.com/2026/03/17/developer-tools/ranking-engineer-agent-rea</a></p></li><li><p><a href="http://engineering.fb.com/2026/04/02/developer-tools/kernelevolve">engineering.fb.com/2026/04/02/developer-tools/kernelevolve</a></p></li><li><p><a href="http://arxiv.org/abs/2512.23236">arxiv.org/abs/2512.23236</a></p></li></ul><h3>Google migration pipeline</h3><p>Fully vertically integrated stack: <strong>Cider V</strong> (VS Code fork on Borg, indexes all of Google3 monorepo) + <strong>Gemini fine-tuned via DIDACT</strong> + <strong>Kythe</strong> code index + <strong>Critique</strong> review tool as the HITL surface. The migration pipeline uses Kythe BFS for deterministic site discovery, fine-tuned Gemini for the diff, deterministic validation cascade (whitespace &#8594; AST &#8594; build &#8594; tests), then per-CL human review in Critique with OWNERS-routing (effectively protected-code HITL via Google&#8217;s existing ownership graph). Example campaigns: int32&#8594;int64 proto migration (595 CLs, 149 teams, 12 months); JUnit3&#8594;JUnit4 (5,359 files, ~149K LOC, 3 months). <strong>Pichai April 2026: 75% of new Google code AI-generated.</strong></p><ul><li><p><a href="http://arxiv.org/abs/2504.09691">arxiv.org/abs/2504.09691</a></p></li><li><p><a href="http://arxiv.org/abs/2601.19964">arxiv.org/abs/2601.19964</a></p></li><li><p><a href="http://blog.google/company-news/inside-google/message-ceo/alphabet-earnings-q1-2026/">blog.google/company-news/inside-google/message-ceo/alphabet-earnings-q1-2026/</a></p></li></ul><h3>Airbnb migration pipeline</h3><p>The &#8220;rent the agent harness, build the repo-harness layer&#8221; pattern. Off-the-shelf Claude Code as the harness, custom internal MCP servers as the context layer &#8212; distinct from Stripe (fork) and Block (own). Two surfaces: engineers running 3&#8211;5 parallel Claude Code CLI sessions on the monorepo, <strong>plus</strong> a standalone batch migration pipeline (per-file state machine: refactor &#8594; fix jest &#8594; fix lint &#8594; fix tsc &#8594; complete; LLM invoked only on gate failure) that migrated <strong>~3,500 Enzyme test files to React Testing Library in 6 weeks</strong> (97% automated, 3% manual long-tail). Chesky Q1 2026: <strong>60% of new code AI-written.</strong></p><ul><li><p><a href="http://airbnb.tech/infrastructure/accelerating-large-scale-test-migration-with-llms/">airbnb.tech/infrastructure/accelerating-large-scale-test-migration-with-llms/</a></p></li><li><p><a href="http://dpe.org/sessions/szczepan-faber-mike-nakhimovich/agentic-coding-at-airbnb">dpe.org/sessions/szczepan-faber-mike-nakhimovich/agentic-coding-at-airbnb</a></p></li><li><p><a href="http://techcrunch.com/2026/05/08/airbnb-says-ai-now-writes-60-of-its-new-code/">techcrunch.com/2026/05/08/airbnb-says-ai-now-writes-60-of-its-new-code/</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[A Framework for AI Dev Tooling: Models, Harnesses, and Loops ]]></title><description><![CDATA[A practical framework for AI development tooling: models, agent harnesses, repo harnesses, and inner, outer, and campaign loops for scaling AI-assisted engineering.]]></description><link>https://jackkora.com/p/a-framework-for-ai-dev-tooling-models</link><guid isPermaLink="false">https://jackkora.com/p/a-framework-for-ai-dev-tooling-models</guid><dc:creator><![CDATA[Jack Kora]]></dc:creator><pubDate>Mon, 22 Jun 2026 21:13:54 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!8hZN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe05e93d7-3aa6-4bc5-ad46-bd018b0cc4ea_1200x655.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8hZN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe05e93d7-3aa6-4bc5-ad46-bd018b0cc4ea_1200x655.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8hZN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe05e93d7-3aa6-4bc5-ad46-bd018b0cc4ea_1200x655.png 424w, https://substackcdn.com/image/fetch/$s_!8hZN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe05e93d7-3aa6-4bc5-ad46-bd018b0cc4ea_1200x655.png 848w, https://substackcdn.com/image/fetch/$s_!8hZN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe05e93d7-3aa6-4bc5-ad46-bd018b0cc4ea_1200x655.png 1272w, https://substackcdn.com/image/fetch/$s_!8hZN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe05e93d7-3aa6-4bc5-ad46-bd018b0cc4ea_1200x655.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8hZN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe05e93d7-3aa6-4bc5-ad46-bd018b0cc4ea_1200x655.png" width="1200" height="655" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e05e93d7-3aa6-4bc5-ad46-bd018b0cc4ea_1200x655.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:655,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A Framework for AI Dev Tooling: Models, Harnesses, and Loops&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A Framework for AI Dev Tooling: Models, Harnesses, and Loops" title="A Framework for AI Dev Tooling: Models, Harnesses, and Loops" srcset="https://substackcdn.com/image/fetch/$s_!8hZN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe05e93d7-3aa6-4bc5-ad46-bd018b0cc4ea_1200x655.png 424w, https://substackcdn.com/image/fetch/$s_!8hZN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe05e93d7-3aa6-4bc5-ad46-bd018b0cc4ea_1200x655.png 848w, https://substackcdn.com/image/fetch/$s_!8hZN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe05e93d7-3aa6-4bc5-ad46-bd018b0cc4ea_1200x655.png 1272w, https://substackcdn.com/image/fetch/$s_!8hZN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe05e93d7-3aa6-4bc5-ad46-bd018b0cc4ea_1200x655.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I feel that there&#8217;s a pattern forming in how cutting-edge engineering orgs use AI tooling to speed up the dev process.</p><p>Two years ago the debate was about models. While models are still important, the LLM harness (this is what Claude Code and Codex CLIs are &#8212; a harness around LLMs) is becoming an important differentiator. Most cutting edge companies have settled on Claude Code or Codex CLI as their primary LLM harness.</p><p>Some quick definitions before we move on.</p><ul><li><p><strong>Coding agent harness</strong> &#8212; the deterministic logic around the LLM itself (Claude Code, Codex CLI). The planning loop, the tool calls, the memory architecture.</p></li><li><p><strong>Repo harness</strong> &#8212; CLAUDE.md / AGENTS.md (with other repo documentation), custom slash commands &amp; skills, linters, automated tests, MCP servers.</p></li></ul><p>Looking at the AI tooling market today, newer products are clearly converging on the repo harness + outer-loop orchestration pattern. Older products &#8212; and in the age of AI, &#8220;older&#8221; means over a year &#8212; are more piecemeal, built as standalone vertical features. Walking through the segments:</p><ul><li><p>Code review segment - Code Rabbit, Qodo, Baz, etc. First-generation products, each with their own LLM, prompting, and harness. My read is that this category is gradually being absorbed into the outer-loop implementation tools below.</p></li><li><p>Implementation segment - Devin, GitHub Copilot Coding Agent, OpenHands, and others. This is where the convergence is most visible &#8212; these tools have grown meaningful repo harness support (AGENTS.md and similar) and outer-loop orchestration primitives, though each still ships its own agent harness. A few tools let you delegate to Claude Code or Codex (Conductor, OpenRig), but they&#8217;re aimed at individuals running the tool on their laptop. OpenHands recently added the option to run frontier coding agents via ACP.</p></li><li><p>Fix code review findings segment - many CI tools, Linear, etc are all jumping into &#8220;press a button and fix your findings&#8221;. This is the one segment where I think the bet is wrong &#8212; piecemeal features running in proprietary agents, disconnected from the dev&#8217;s primary loop. Not what developers actually need.</p></li></ul><p>The bigger story, though, is what the companies that have figured this out are building themselves. Stripe, Shopify, Uber, Airbnb have all talked publicly about their internal AI dev platforms. These orgs are not bolting on piecemeal vendor features &#8212; they&#8217;re building full outer-loop orchestration that integrates one agent harness across local dev, cloud agents, and review. Smaller cutting-edge teams I&#8217;ve talked to are doing the same (but they don&#8217;t post this online). The reason is always the same: integrate the agent into your process, don&#8217;t import someone else&#8217;s.</p><p>A couple more definitions for the AI development process. The loops are best defined by how often the developer has to check in with the AI. Note that where the loop runs (on your laptop, in the cloud) is not important - it&#8217;s the interaction model between the builder and AI that is the defining factor.</p><ul><li><p><strong>Inner loop</strong> &#8212; conversational and real-time. Check-ins happen in seconds to minutes. Even when you ask the agent to do research or a small implementation and wait a few minutes for it to come back, that&#8217;s still part of the conversation. Typically this is your laptop (but not necessarily): ticket/task definition &amp; architecture, exploration, smaller implementations.</p></li><li><p><strong>Outer loop</strong> &#8212; not conversational. Check-ins happen in 30-minute to multi-hour increments. The agent runs, hits a gate, asks for input, and runs again. Longer implementation tasks, PR reviews &amp; fixing findings, investigating production incidents &amp; error spikes, recurring tasks (security/architecture drifts, analyzing new CVEs, etc).</p></li><li><p><strong>Campaign loop</strong> &#8212; also HITL-based, but the goals span days to weeks. Think: &#8220;migrate this service off the old framework,&#8221; &#8220;harden auth across the platform.&#8221; This is a much more advanced concept and the industry is barely starting to figure it out. I won&#8217;t focus on it here, but it&#8217;s worth naming because it&#8217;s coming.</p></li></ul><p><strong>The core principle: same agent harness, same repo harness, and same context across both loops. And every handoff between inner and outer should be warm-context.</strong></p><p>There&#8217;s one more axis worth getting right: the interaction model for outer-loop agents. Platforms like OpenHands offer both &#8212; workflow-style entry points (GitHub labels, Slack mentions, CLI) and a conversational dashboard you can sit in front of. The principle I lean toward: conversation is what the inner loop is for. If I&#8217;m going to iterate with an agent in natural language, I&#8217;d rather do it on my laptop, where the full repo harness is loaded and the feedback loop is tight.</p><p>The outer loop calls for something different: deterministic workflows that incorporate AI as a step, with explicit HITL gates at the surfaces where work already happens &#8212; GitHub PRs, Linear tickets, Slack threads. The market is moving this way &#8212; Devin Playbooks, GitHub Copilot&#8217;s <code>.github/agents/</code> profiles, OpenHands hooks and skills are all takes on this.</p><p>An outer-loop code review workflow could look like this:</p><ul><li><p>On PR open, AI runs the initial review and posts findings as inline GitHub comments.</p></li><li><p>AI responds to developer questions and &#8220;skip this&#8221; instructions through the GitHub UI &#8212; same surface, no new tool.</p></li><li><p>AI confirms &#8220;this is fixed&#8221; comments and does incremental reviews on new commits.</p></li><li><p>AI auto-approves the PR when conditional gates pass: all findings addressed, risk below threshold, no sensitive modules touched.</p></li></ul><p>One honest caveat on the outer loop: the framework is sound, but the devil is in two related problems. First, <strong>HITL design</strong> &#8212; you can&#8217;t just let these agents run unattended, but where exactly do you build in the gates? When should the agent stop and ask vs. push through? The market is actively exploring this &#8212; confidence-driven (Devin&#8217;s self-grading), single-gate (Copilot&#8217;s PR-only review), hook-driven (OpenHands milestones) &#8212; each with different trade-offs. Second, context &#8212; <strong>how does context carry across HITL moments</strong> and across the agent&#8217;s run so it doesn&#8217;t lose the thread between gates? Both topics are meaty enough that I&#8217;ll dig into them in a follow-up article.</p><p>To summarize - there are three concepts to take away:</p><ul><li><p><strong>LLMs &#8800; agent harness &#8800; repo harness</strong>. The model is the engine. The agent harness (Claude Code, Codex CLI) is the loop around it. The repo harness (CLAUDE.md, skills, MCP servers, tests, linters, etc) is the project context that makes either useful.</p></li><li><p><strong>Three loops, defined by check-in frequency</strong>. Inner loop is conversational, minute-scale &#8212; your laptop. Outer loop is HITL-based, 30-min to multi-hour &#8212; cloud agents on bounded tasks. Campaign loop is HITL-based, days to weeks &#8212; advanced territory, not the focus here. The agent harness, repo harness, and MCPs should be identical across inner and outer.</p></li><li><p><strong>Conversation in the inner loop. Workflows in the outer loop</strong>. Chatting with an agent on your laptop is the right inner-loop UX. Cloud agents should run deterministic workflows with AI incorporated &amp; HITL gates at the surfaces where work already happens.</p></li></ul><p>The market is still organizing around these distinctions. If you&#8217;re building or evaluating AI dev tooling, these three concepts are useful axes &#8212; both for thinking about your own stack and for the conversations you have with your vendors. </p><div class="callout-block" data-callout="true"><p><span>See my next post &#8212; </span><a href="https://jackkora.com/p/mapping-the-ai-coding-orchestrator">Mapping the AI coding orchestrator market</a> &#8212; where I apply this framework to the current market landscape for coding orchestrators.</p></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://jackkora.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Managing & coaching personal conflict]]></title><description><![CDATA[The second day after I joined a new company one of my new reports burst into tears at a seemingly innocuous &#8220;How is it going?&#8221; at the beginning of our first one-on-one.]]></description><link>https://jackkora.com/p/managing-and-coaching-personal-conflict</link><guid isPermaLink="false">https://jackkora.com/p/managing-and-coaching-personal-conflict</guid><dc:creator><![CDATA[Jack Kora]]></dc:creator><pubDate>Sun, 17 Jan 2021 18:12:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!NPYd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46cd7640-ac32-4b45-bf58-678d96b4593b_960x640.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NPYd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46cd7640-ac32-4b45-bf58-678d96b4593b_960x640.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NPYd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46cd7640-ac32-4b45-bf58-678d96b4593b_960x640.jpeg 424w, https://substackcdn.com/image/fetch/$s_!NPYd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46cd7640-ac32-4b45-bf58-678d96b4593b_960x640.jpeg 848w, https://substackcdn.com/image/fetch/$s_!NPYd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46cd7640-ac32-4b45-bf58-678d96b4593b_960x640.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!NPYd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46cd7640-ac32-4b45-bf58-678d96b4593b_960x640.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NPYd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46cd7640-ac32-4b45-bf58-678d96b4593b_960x640.jpeg" width="960" height="640" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/46cd7640-ac32-4b45-bf58-678d96b4593b_960x640.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:640,&quot;width&quot;:960,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Personal Conflicts At Work: Catalyst Or Catastrophe&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Personal Conflicts At Work: Catalyst Or Catastrophe" title="Personal Conflicts At Work: Catalyst Or Catastrophe" srcset="https://substackcdn.com/image/fetch/$s_!NPYd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46cd7640-ac32-4b45-bf58-678d96b4593b_960x640.jpeg 424w, https://substackcdn.com/image/fetch/$s_!NPYd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46cd7640-ac32-4b45-bf58-678d96b4593b_960x640.jpeg 848w, https://substackcdn.com/image/fetch/$s_!NPYd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46cd7640-ac32-4b45-bf58-678d96b4593b_960x640.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!NPYd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46cd7640-ac32-4b45-bf58-678d96b4593b_960x640.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The second day after I joined a new company one of my new reports burst into tears at a seemingly innocuous &#8220;How is it going?&#8221; at the beginning of our first one-on-one. Though I was vaguely aware (it was my second day) that there were some bad vibes on the team, this emotional reaction caught me completely by surprise.</p><h2><strong>The story</strong></h2><p>I was not prepared for what had happened at the one-on-one and I almost panicked in the moment. Luckily, I remembered a management article that talked about similar situations and how it&#8217;s important to just listen. So I focused on listening and empathizing without trying to jump to solutions. It took me a few more meetings to better understand the whole picture.</p><p>This person was an engineer with about four years of experience, who had to deal with very direct and matter-of-fact feedback from one of the more senior engineers. In addition, she frequently felt talked over, disrespected, and never listened to in general. I&#8217;d like to add (as a side note), that this is a somewhat common problem in engineering where juniors (who are naturally less confident in their tech skills) feel disrespected when faced with a lot of direct feedback. And being a female in tech is also a factor to be mindful of.</p><p>After chatting with the senior engineer, I came away with the feeling that he didn&#8217;t mean to be harsh or disrespectful, but at the same time, it was obvious that he could improve on how he delivers his feedback. Over the next couple of months, I worked with him on adding empathy and understanding to his communication style.</p><p>This helped some and at the same time, I started to coach the junior on how to receive critical feedback and on some of the work processes that she could leverage to overcome conflict. For example, instead of waiting for feedback during a code review (which may ask the author to rewrite significant portions of the code), I pushed the junior developer to seek design feedback earlier (design reviews, etc).</p><p>Over time I also introduced several other process changes like:</p><ul><li><p>Everyone gets a chance to speak during group discussions (as opposed to only the loudest voices in the room).</p></li><li><p>Ideas are debated around their pros/cons and trade-offs instead of around being good or bad.</p></li><li><p>If a debate gets too heated (we&#8217;re all human after all), take a break and come back to it in a few hours or the next day. It has the additional benefit of being able to sleep on the ideas and get a better or a new understanding of them.</p></li></ul><p>Our regular coaching sessions and my willingness to listen helped the junior engineer improve her skills, but more importantly, gain trust and confidence in herself and the team. She became more comfortable with professional conflict and debating various engineering decisions. She also learned to be less and less stressed while doing so.</p><p>There was an occasion that I&#8217;d like to highlight where I basically had to pull rank. I and another very senior engineer had to override the junior&#8217;s technical idea on a fairly complex topic after a long and heated debate. I had a follow-up chat with her and we chatted about how this made her feel like a junior. Towards the end of the conversation (and after listening), I pointed out that the very senior dev and I have 40 years of experience combined vs. her five years. And while I don&#8217;t like using this as an argument, sometimes it&#8217;s very hard to explain the totality of your experience and why it necessitates certain decisions (but you should try anyway). That conversation (including my remark) ended well and I attribute that to the trust I&#8217;ve built up to that point.</p><p>Starting with our initial one-on-one, the junior engineer made steady progress and over time became one of the star performers on the team who wasn&#8217;t shy of getting into debates and pushing for her opinion (in a productive kind of way). I was proud of her accomplishments and, at a later date, she went on to work for a major Silicon Valley company.</p><h2><strong>What I&#8217;ve learned</strong></h2><p>Writing this blog, it feels like some of these lessons are obvious and maybe even trite. And yet, in the moment, it can be hard to remember them and apply them in practice. I hope that this little summary is at least a helpful reminder.</p><ul><li><p>Listen. You don&#8217;t have to try and fix &#8220;it&#8221; now. Frequently the important part is to listen and empathize. Which helps to build trust in the longer term.</p></li><li><p>Do problem solve. But at a later time and day, after you&#8217;ve listened, established some trust, and gathered feedback and context from multiple sources.</p></li><li><p>Delivering feedback well can be learned. Like all other soft/people/EQ skills it&#8217;s not the easiest to learn but it&#8217;s possible. You can be both direct and empathetic at the same time (<a href="https://www.radicalcandor.com/">radical candor</a> ftw). Invest time in teaching developers how to do it.</p></li><li><p>Set good (equitable) team practices. Everyone needs a chance to speak, decisions need to be transparent, etc. This is helpful for everyone, not just juniors.</p></li><li><p>Pulling rank is ok. But only <em>very</em> occasionally. And only after you discuss the decision first and explain why you chose to pull rank in the end. And that assumes that you&#8217;ve already built some trust that you can rely on.</p></li><li><p>Personal change takes time. This whole story took place over a two year period. As a manager you need to be patient and supportive throughout.</p></li></ul>]]></content:encoded></item><item><title><![CDATA[Bias to action & minimum timeboxing]]></title><description><![CDATA[Like many people in the tech space, I am biased to action.]]></description><link>https://jackkora.com/p/bias-to-action-and-minimum-timeboxing</link><guid isPermaLink="false">https://jackkora.com/p/bias-to-action-and-minimum-timeboxing</guid><dc:creator><![CDATA[Jack Kora]]></dc:creator><pubDate>Mon, 05 Aug 2019 23:23:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!EA1i!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F082f3aa7-7c89-4138-a409-a80f93b6aa90_1445x492.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!EA1i!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F082f3aa7-7c89-4138-a409-a80f93b6aa90_1445x492.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EA1i!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F082f3aa7-7c89-4138-a409-a80f93b6aa90_1445x492.png 424w, https://substackcdn.com/image/fetch/$s_!EA1i!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F082f3aa7-7c89-4138-a409-a80f93b6aa90_1445x492.png 848w, https://substackcdn.com/image/fetch/$s_!EA1i!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F082f3aa7-7c89-4138-a409-a80f93b6aa90_1445x492.png 1272w, https://substackcdn.com/image/fetch/$s_!EA1i!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F082f3aa7-7c89-4138-a409-a80f93b6aa90_1445x492.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!EA1i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F082f3aa7-7c89-4138-a409-a80f93b6aa90_1445x492.png" width="1445" height="492" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/082f3aa7-7c89-4138-a409-a80f93b6aa90_1445x492.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:492,&quot;width&quot;:1445,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:653568,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://jackkora.com/i/209174267?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F082f3aa7-7c89-4138-a409-a80f93b6aa90_1445x492.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!EA1i!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F082f3aa7-7c89-4138-a409-a80f93b6aa90_1445x492.png 424w, https://substackcdn.com/image/fetch/$s_!EA1i!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F082f3aa7-7c89-4138-a409-a80f93b6aa90_1445x492.png 848w, https://substackcdn.com/image/fetch/$s_!EA1i!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F082f3aa7-7c89-4138-a409-a80f93b6aa90_1445x492.png 1272w, https://substackcdn.com/image/fetch/$s_!EA1i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F082f3aa7-7c89-4138-a409-a80f93b6aa90_1445x492.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Like many people in the tech space, I am biased to action. It is a generally a great trait that helps to move forward and get things done. And it just feels good! Being a manager I also feel the extra need for speed since projects inevitably slow down when more and more people get involved.</p><p>It&#8217;s a trait that I was naturally imbued with and that I further developed through my career. It&#8217;s a resume builder and something that&#8217;s typically sought after in managers.</p><p><strong>what is minimum timeboxing?</strong></p><p>Yet, there are situations and problems where deliberately slowing down is necessary. It is necessary to ensure you can evaluate all the ideas. It is necessary so you can &#8220;sleep on it&#8221; and let it brew in your head. It is necessary so you can reflect on the problem itself (in the words of my manager &#8220;to live in the problem space&#8221;). It is necessary so others who are involved can reflect on it on their own timeline.</p><p>So how do you do this without compromising your core bias to action? How do you reconcile the two? For someone with a bias to action it is hard to be still and not act. I know it is for me.</p><p>Minimum timeboxing is a technique I came up with recently to address this. <a href="https://medium.com/dreimannzelt-adventures/7-secrets-to-master-timeboxing-66a744ea9175">Timeboxing</a>, a well established personal productivity and project management technique, helps to move things along. It helps to be more biased to action by limiting how much you spend on a decision.</p><p>Minimum timeboxing, takes the same approach but defines the <em>minimum</em> time you must spend on a decision. In fact, you can combine the two and specify both the minimum and the maximum time.</p><p><strong>where and how do you use it?</strong></p><p>There isn&#8217;t more to this technique &#8212; you can start using it right away. But I want to dive deeper into why and when it is important to sometimes not move fast, especially in times when you feel the extra push.</p><p>A great place to start is with the classic decision matrix below.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!F_Br!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37b6c0d0-fa7d-4fa5-b4c4-749616086b36_412x411.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!F_Br!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37b6c0d0-fa7d-4fa5-b4c4-749616086b36_412x411.png 424w, https://substackcdn.com/image/fetch/$s_!F_Br!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37b6c0d0-fa7d-4fa5-b4c4-749616086b36_412x411.png 848w, https://substackcdn.com/image/fetch/$s_!F_Br!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37b6c0d0-fa7d-4fa5-b4c4-749616086b36_412x411.png 1272w, https://substackcdn.com/image/fetch/$s_!F_Br!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37b6c0d0-fa7d-4fa5-b4c4-749616086b36_412x411.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!F_Br!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37b6c0d0-fa7d-4fa5-b4c4-749616086b36_412x411.png" width="412" height="411" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/37b6c0d0-fa7d-4fa5-b4c4-749616086b36_412x411.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:411,&quot;width&quot;:412,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!F_Br!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37b6c0d0-fa7d-4fa5-b4c4-749616086b36_412x411.png 424w, https://substackcdn.com/image/fetch/$s_!F_Br!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37b6c0d0-fa7d-4fa5-b4c4-749616086b36_412x411.png 848w, https://substackcdn.com/image/fetch/$s_!F_Br!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37b6c0d0-fa7d-4fa5-b4c4-749616086b36_412x411.png 1272w, https://substackcdn.com/image/fetch/$s_!F_Br!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37b6c0d0-fa7d-4fa5-b4c4-749616086b36_412x411.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The decisions that are high impact and hard to undo should definitely have a minimum timebox. The ones in the upper left and lower right quadrants should be seriously considered as well, while the low impact / easy to undo are good candidates for moving fast.</p><p>Decision matrix aside sometimes there is urgency around problems but before you jump into action take some time to think where this urgency comes from. While some decisions are truly urgent, you should ask yourself is the urgency real? Is it self-generated due to your stress / bias to action / subconscious want to just-be-done-with-it?</p><p>If you are a problem solver like me and like to move fast, there is a good chance that you are feeling these urges. On the other hand, could they be coming from someone else in the organization who is dealing with similar emotions?</p><p>Using minimum timeboxing is a way to give yourself a chance to get immersed in the problem and to ensure that you don&#8217;t jump to a rushed decision.</p><p><em>P.S. I&#8217;m sure someone somewhere already thought of this idea but I personally haven&#8217;t heard of it.</em></p><div><hr></div><p>If you&#8217;re an engineer, check out this article by Rich Hickey (creator of Clojure) that is on a very much related topic &#8212; <a href="https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/HammockDrivenDev.md">Step Away from the Computer or Hammock-driven Development</a>.</p>]]></content:encoded></item><item><title><![CDATA[Denver Startup Week — the Ambassador experience]]></title><description><![CDATA[Let me start by saying just how much I enjoyed the entire trip.]]></description><link>https://jackkora.com/p/denver-startup-week-the-ambassador</link><guid isPermaLink="false">https://jackkora.com/p/denver-startup-week-the-ambassador</guid><dc:creator><![CDATA[Jack Kora]]></dc:creator><pubDate>Thu, 03 Jan 2019 23:04:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!rv7J!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb0023ac-5b3d-4eaf-8286-62af957e374c_1920x1080.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rv7J!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb0023ac-5b3d-4eaf-8286-62af957e374c_1920x1080.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rv7J!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb0023ac-5b3d-4eaf-8286-62af957e374c_1920x1080.jpeg 424w, https://substackcdn.com/image/fetch/$s_!rv7J!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb0023ac-5b3d-4eaf-8286-62af957e374c_1920x1080.jpeg 848w, https://substackcdn.com/image/fetch/$s_!rv7J!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb0023ac-5b3d-4eaf-8286-62af957e374c_1920x1080.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!rv7J!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb0023ac-5b3d-4eaf-8286-62af957e374c_1920x1080.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rv7J!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb0023ac-5b3d-4eaf-8286-62af957e374c_1920x1080.jpeg" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/db0023ac-5b3d-4eaf-8286-62af957e374c_1920x1080.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Denver, Colorado Aerial Photography | FLY.PHOTOS&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Denver, Colorado Aerial Photography | FLY.PHOTOS" title="Denver, Colorado Aerial Photography | FLY.PHOTOS" srcset="https://substackcdn.com/image/fetch/$s_!rv7J!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb0023ac-5b3d-4eaf-8286-62af957e374c_1920x1080.jpeg 424w, https://substackcdn.com/image/fetch/$s_!rv7J!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb0023ac-5b3d-4eaf-8286-62af957e374c_1920x1080.jpeg 848w, https://substackcdn.com/image/fetch/$s_!rv7J!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb0023ac-5b3d-4eaf-8286-62af957e374c_1920x1080.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!rv7J!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb0023ac-5b3d-4eaf-8286-62af957e374c_1920x1080.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Let me start by saying just how much I enjoyed the entire trip. The city of Denver. The local startups. The hosts. The itinerary. And my fellow participants.</p><p>The goal of the Denver Startup Week is to promote the local startup community as well as the city of Denver at the national level. It&#8217;s been running for seven years straight now. The Ambassador program, however, is only two years old and was created to promote Denver as a high tech hub and a viable alternative to the Valley both for work and high quality living. By partnering with Denver startups and more established companies (thank you Southwest Airlines) the program brings 50 Ambassadors from across the country to experience Denver and network with the local startup community.</p><p>Let me start with sharing my impressions of Denver. Despite having been to the Denver Airport only about 50 times (I&#8217;m an avid skier and midwest just does not cut it), I have never managed to visit Denver itself. I was always focused on maximizing my slope time and I was excited to finally roam around Denver. And I liked what I saw. Really liked! Living downtown Chicago I am very much a city person and Denver certainly catered to my taste. It&#8217;s a city, it&#8217;s got a city vibe, it&#8217;s got graffiti (the artistic kind), it&#8217;s got coffee shops, restaurants, rental bicycles and scooters (first time I saw those), and it&#8217;s got people.</p><p>It&#8217;s more laid back and more friendly than Chicago, it shuts down earlier, and it&#8217;s just noticeably smaller. But it&#8217;s got all the right parts and it&#8217;s growing! About 10,000 people move to Denver monthly. It&#8217;s also the sunniest city in the States with 300 sun days a year and, contrary to what you might think, it&#8217;s warm with the average winter temp way above Chicago&#8217;s.</p><p>And Denver has mountains. Mountains with some of the best snow on this planet.</p><p>I was also surprised by how developed the startup scene is in Denver. We had the opportunity to have at length meetups with several startups. We heard presentations and talked with CEOs and other executives, techies, marketers &#8212; you name it. Given an opportunity, I would love to work with any of these people. And it&#8217;s not just the number of startups, it&#8217;s the scale, and the professional experience that I was impressed with.</p><p>One thing I would like to call out specifically is the gender diversity that stood out to me. It felt to be more mixed than Chicago and that&#8217;s something that I hope we in Chicago can catch up with soon.</p><p>While Denver might not be for everyone, it&#8217;s great to see that Valley&#8217;s monopoly on tech and tech startups is beginning to wane.</p><p>Thank you Denver Startup Week for the wonderful experience!</p>]]></content:encoded></item><item><title><![CDATA[Delivering on long term roadmap: myth or reality?]]></title><description><![CDATA[When we plan a roadmap several quarters or a whole year ahead what are our expectations (and those of the leadership team)?]]></description><link>https://jackkora.com/p/delivering-on-long-term-roadmap</link><guid isPermaLink="false">https://jackkora.com/p/delivering-on-long-term-roadmap</guid><dc:creator><![CDATA[Jack Kora]]></dc:creator><pubDate>Sun, 25 Feb 2018 23:15:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!5PBD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddbe1ad8-e59f-44ae-8e2a-da11b1e80b1b_1280x652.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5PBD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddbe1ad8-e59f-44ae-8e2a-da11b1e80b1b_1280x652.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5PBD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddbe1ad8-e59f-44ae-8e2a-da11b1e80b1b_1280x652.png 424w, https://substackcdn.com/image/fetch/$s_!5PBD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddbe1ad8-e59f-44ae-8e2a-da11b1e80b1b_1280x652.png 848w, https://substackcdn.com/image/fetch/$s_!5PBD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddbe1ad8-e59f-44ae-8e2a-da11b1e80b1b_1280x652.png 1272w, https://substackcdn.com/image/fetch/$s_!5PBD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddbe1ad8-e59f-44ae-8e2a-da11b1e80b1b_1280x652.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5PBD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddbe1ad8-e59f-44ae-8e2a-da11b1e80b1b_1280x652.png" width="1280" height="652" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ddbe1ad8-e59f-44ae-8e2a-da11b1e80b1b_1280x652.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:652,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Customizable Gantt Chart Makers - Ganttic&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Customizable Gantt Chart Makers - Ganttic" title="Customizable Gantt Chart Makers - Ganttic" srcset="https://substackcdn.com/image/fetch/$s_!5PBD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddbe1ad8-e59f-44ae-8e2a-da11b1e80b1b_1280x652.png 424w, https://substackcdn.com/image/fetch/$s_!5PBD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddbe1ad8-e59f-44ae-8e2a-da11b1e80b1b_1280x652.png 848w, https://substackcdn.com/image/fetch/$s_!5PBD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddbe1ad8-e59f-44ae-8e2a-da11b1e80b1b_1280x652.png 1272w, https://substackcdn.com/image/fetch/$s_!5PBD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddbe1ad8-e59f-44ae-8e2a-da11b1e80b1b_1280x652.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>When we plan a roadmap several quarters or a whole year ahead what are our expectations (and those of the leadership team)? Frequently the implicit and subconscious expectation is that everything will get done. After all if we use <a href="https://medium.com/@jkorabelnikov/scope-on-time-delivery-adb8382b0a99">good scoping techniques</a> we should be able to hit every deadline. Right?</p><p>Not quite. We might choose to extend the scope of some projects. We might remove some projects and add others, with different scoping, all due to market demand. And what about that quick marketing change that&#8217;s so needed but wasn&#8217;t planned for or an internal tools improvement that will shave off 20% of operational costs from your account management team? If our plans are too rigid we won&#8217;t be able to react to the outside changes and might miss good business opportunities.</p><p>Another reason why it&#8217;s typical to underdeliver on the roadmap is the one-sided variability in planned dates. A team that&#8217;s already pushing hard will almost never deliver projects ahead of time (but will sometimes run over the original plan). So whatever variability there is will add up and will eventually mean that a project or two won&#8217;t get done.</p><p>A good way to look at long term roadmapping is the same way as any other goal setting exercise. <a href="https://en.wikipedia.org/wiki/OKR">OKRs</a> and <a href="https://www.eosworldwide.com/">EOS</a>, two popular organizational methodologies, are both heavy on settings goals and both say that you should aim at about 80% achievement. Because if you hit 100% of your goals they weren&#8217;t ambitious enough and you didn&#8217;t push yourself (or your org) hard enough.</p><p>However, let&#8217;s not walk away from this thinking that roadmap planning is useless. On the contrary, we should deliberately plan roadmaps that are a (reasonable) stretch and push ourselves to deliver as much as possible. Like we do with all other goals.</p>]]></content:encoded></item><item><title><![CDATA[Scope & on time delivery]]></title><description><![CDATA[&#8220;What if the customer clicks on this button, sees the popup, types some text, then cancels, and later comes back to the popup &#8212; do we preserve the previously typed text?&#8221;]]></description><link>https://jackkora.com/p/scope-and-on-time-delivery</link><guid isPermaLink="false">https://jackkora.com/p/scope-and-on-time-delivery</guid><dc:creator><![CDATA[Jack Kora]]></dc:creator><pubDate>Mon, 05 Feb 2018 21:34:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!P-JM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fdbe34b-b868-43a5-9706-556f4e798830_1024x434.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!P-JM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fdbe34b-b868-43a5-9706-556f4e798830_1024x434.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!P-JM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fdbe34b-b868-43a5-9706-556f4e798830_1024x434.png 424w, https://substackcdn.com/image/fetch/$s_!P-JM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fdbe34b-b868-43a5-9706-556f4e798830_1024x434.png 848w, https://substackcdn.com/image/fetch/$s_!P-JM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fdbe34b-b868-43a5-9706-556f4e798830_1024x434.png 1272w, https://substackcdn.com/image/fetch/$s_!P-JM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fdbe34b-b868-43a5-9706-556f4e798830_1024x434.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!P-JM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fdbe34b-b868-43a5-9706-556f4e798830_1024x434.png" width="1024" height="434" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8fdbe34b-b868-43a5-9706-556f4e798830_1024x434.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:434,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:274784,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://jackkora.com/i/209173421?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fdbe34b-b868-43a5-9706-556f4e798830_1024x434.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!P-JM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fdbe34b-b868-43a5-9706-556f4e798830_1024x434.png 424w, https://substackcdn.com/image/fetch/$s_!P-JM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fdbe34b-b868-43a5-9706-556f4e798830_1024x434.png 848w, https://substackcdn.com/image/fetch/$s_!P-JM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fdbe34b-b868-43a5-9706-556f4e798830_1024x434.png 1272w, https://substackcdn.com/image/fetch/$s_!P-JM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fdbe34b-b868-43a5-9706-556f4e798830_1024x434.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#8220;What if the customer clicks on this button, sees the popup, types some text, then cancels, and later comes back to the popup &#8212; do we preserve the previously typed text?&#8221;</p><p>This is a typical conversation that happens during product / design / technical discussion on software engineering teams. Inadvertently, the answer is an enthusiastic &#8220;yes&#8221;. Everyone wants to deliver an awesome product that&#8217;s thought out through and through. And therein lies the danger &#8212; we get carried away and blow up the scope of work.</p><p>According to Mary Poppendieck&#8217;s &#8220;<a href="https://www.amazon.com/Leading-Lean-Software-Development-Results/dp/0321620704">Leading Lean Software Development</a>&#8221;, 60% of released features are never used by end users. Extensive user research, user interviewing, and such helps to narrow things down, however it&#8217;s an imprecise science. Users will typically ask for everything (because why wouldn&#8217;t you), but they can&#8217;t tell us what&#8217;s a must have vs. a nice to have. They may genuinely believe that they need a certain feature only to never use it when it comes out.</p><p>For many businesses it&#8217;s more important to deliver a core set of functionality and move on than to spend time on fluffy features and questionable edge cases that might never be used. It&#8217;s also good to remember that if we end up missing some needed feature we can always add it later.</p><p>In project management there is the holy trinity of scope, time, and quality and we can only choose two out of the three. Cutting scope is an underused technique that needs to be talked about more. Cutting quality is not a good idea (modern users expect top notch products) and so is making the team work consistent overtime (lest we want to lose team productivity and potentially the team itself).</p><p>It frequently seems impossible to cut scope but if we really put our mind to it, we can find a few minor features or edge-cases that will shave 5&#8211;15% off of scope. Done consistently this will increase the average team productivity and allow us to kick out more projects. Moreover, we can timebox our projects, which will let us deliver all (or almost all) of the important, high visibility features in our yearly roadmap.</p><h3><strong>project structuring</strong></h3><p>Given the <a href="https://medium.com/@jkorabelnikov/on-productivity-predictability-in-software-engineering-54426a557ad7">unpredictable nature of software engineering</a>, how do we know when and what scope to cut? Maybe we can fit everything into the given project&#8217;s timeline? The truth is that we do not know upfront.</p><p>As we start working on a project, we need to first deliver the must have features. As we get closer to the end of the timebox, we can discuss the remaining features and make an informed decision on which ones we are able and want to finish and which ones we can drop.</p><p>There is a wealth of information on this technique available online, known as vertical slicing in Agile circles.</p>]]></content:encoded></item><item><title><![CDATA[On productivity & predictability in software engineering]]></title><description><![CDATA[&#8220;I am completely satisfied with how fast our engineering team delivers and how well they hit the deadlines,&#8221; said no CEO ever.]]></description><link>https://jackkora.com/p/on-productivity-and-predictability</link><guid isPermaLink="false">https://jackkora.com/p/on-productivity-and-predictability</guid><dc:creator><![CDATA[Jack Kora]]></dc:creator><pubDate>Mon, 05 Feb 2018 21:23:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!netX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2874fa97-0c7f-4938-86de-c7ff9bab8516_577x350.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!netX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2874fa97-0c7f-4938-86de-c7ff9bab8516_577x350.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!netX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2874fa97-0c7f-4938-86de-c7ff9bab8516_577x350.png 424w, https://substackcdn.com/image/fetch/$s_!netX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2874fa97-0c7f-4938-86de-c7ff9bab8516_577x350.png 848w, https://substackcdn.com/image/fetch/$s_!netX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2874fa97-0c7f-4938-86de-c7ff9bab8516_577x350.png 1272w, https://substackcdn.com/image/fetch/$s_!netX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2874fa97-0c7f-4938-86de-c7ff9bab8516_577x350.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!netX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2874fa97-0c7f-4938-86de-c7ff9bab8516_577x350.png" width="577" height="350" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2874fa97-0c7f-4938-86de-c7ff9bab8516_577x350.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:350,&quot;width&quot;:577,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Paying the predictability tax. | Irrational Exuberance&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Paying the predictability tax. | Irrational Exuberance" title="Paying the predictability tax. | Irrational Exuberance" srcset="https://substackcdn.com/image/fetch/$s_!netX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2874fa97-0c7f-4938-86de-c7ff9bab8516_577x350.png 424w, https://substackcdn.com/image/fetch/$s_!netX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2874fa97-0c7f-4938-86de-c7ff9bab8516_577x350.png 848w, https://substackcdn.com/image/fetch/$s_!netX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2874fa97-0c7f-4938-86de-c7ff9bab8516_577x350.png 1272w, https://substackcdn.com/image/fetch/$s_!netX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2874fa97-0c7f-4938-86de-c7ff9bab8516_577x350.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#8220;I am completely satisfied with how fast our engineering team delivers and how well they hit the deadlines,&#8221; said no CEO ever.</p><p>We all want to deliver faster and hit those upfront estimates or deadlines. But is that a goal that we want to impose on engineering teams? Is that a goal that benefits the business as a whole? To understand this issue we need to look at it in terms of cost / benefit analysis and we need to delineate the difference between productivity and predictability.</p><h3><strong>Productivity vs. predictability</strong></h3><p>Productivity is simply how fast can a team deliver. Predictability is how accurately the team can estimate project timelines. While productivity is largely a factor of team members individual skills and the overall team cohesion (and team size), predictability more so depends on external factors. Software engineering is inherently unpredictable due to a multitude of possible unforeseen technical issues and requirement gaps discovered in the middle of project implementation. Sales, marketing, contract negotiation, and other business functions are similarly unpredictable &#8212; there are too many external factors that are impossible to account for upfront. If we really think about it, life in general is quite unpredictable.</p><p>But of course a business cannot operate without some degree of predictability. Instead of looking at it as a binary &#8212; can we hit deadlines or not &#8212; we need to look at it as a range of how much we are willing to pay for an extra degree of certainty.</p><p>So what is the cost of predictability? Unfortunately the cost of predictability is productivity. For a project to have more predictable timeline it needs to be discussed, designed, prototyped, and researched extensively before an estimate can be produced. That work takes away from the current project being worked on. It is tempting to think that all this upfront work needs to be done for the new project anyway and as such goes towards its eventual implementation, however this is only partially true. While some of this work is reusable, some inadvertently is not. The biggest hit, however, comes from context switching the engineering team, which happens twice. Once, as the team switches away from the current project and another time switching back to the current project. Moreover, by the time the new project starts many findings from research have faded from memory and need to be re-learned causing more wasted time.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!V4Z0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c2d220-64af-4012-ab21-bc4a41cf1848_250x238.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!V4Z0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c2d220-64af-4012-ab21-bc4a41cf1848_250x238.png 424w, https://substackcdn.com/image/fetch/$s_!V4Z0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c2d220-64af-4012-ab21-bc4a41cf1848_250x238.png 848w, https://substackcdn.com/image/fetch/$s_!V4Z0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c2d220-64af-4012-ab21-bc4a41cf1848_250x238.png 1272w, https://substackcdn.com/image/fetch/$s_!V4Z0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c2d220-64af-4012-ab21-bc4a41cf1848_250x238.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!V4Z0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c2d220-64af-4012-ab21-bc4a41cf1848_250x238.png" width="250" height="238" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/22c2d220-64af-4012-ab21-bc4a41cf1848_250x238.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:238,&quot;width&quot;:250,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!V4Z0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c2d220-64af-4012-ab21-bc4a41cf1848_250x238.png 424w, https://substackcdn.com/image/fetch/$s_!V4Z0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c2d220-64af-4012-ab21-bc4a41cf1848_250x238.png 848w, https://substackcdn.com/image/fetch/$s_!V4Z0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c2d220-64af-4012-ab21-bc4a41cf1848_250x238.png 1272w, https://substackcdn.com/image/fetch/$s_!V4Z0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c2d220-64af-4012-ab21-bc4a41cf1848_250x238.png 1456w" sizes="100vw"></picture><div></div></div></a><figcaption class="image-caption">predictability vs. productivity</figcaption></figure></div><p>The good news is that if we graphed time investment (i.e., productivity) vs. predictability it would look something like the graph on the left. What that means in practice is that we can get a good level of predictability for a moderate time investment, however if we want precise dates it will require a larger time investment and a larger hit to productivity.</p><h3><strong>Roadmapping &amp; project management</strong></h3><p>Now that we understand the inverse relationship between productivity and predictability we can make informed business decisions. Some projects (and some business domains) require more predictability (e.g., contractual obligations, date related business opportunities, etc), however for most e-commerce businesses a week or two of difference on individual project delivery don&#8217;t matter. What matters is how quickly on average projects are delivered (e.g., how many projects per year a team can complete).</p><p>What we typically want to optimize for is quicker average delivery with acceptable predictability. A mature software team can predict projects with 50% accuracy with minimal time investment while still focusing on the project at hand. For those few projects that do require hard dates, we can make exceptions ahead of time and plan on doing more research and design.</p><p>With this approach in mind, the engineering team can move fast with the acceptable level of predictability. Some projects are on time and some take a week or two longer &#8212; not a big deal for each project&#8212; but over time the weeks accumulate and we soon realize that there is no way we will get to the last 2&#8211;3 projects on our yearly roadmap. And of course demanding more predictability will only backfire.</p><p>So what do we do? Take a look at another article titled &#8220;<a href="https://medium.com/@jkorabelnikov/scope-on-time-delivery-adb8382b0a99">scope &amp; on time delivery</a>&#8221;.</p>]]></content:encoded></item><item><title><![CDATA[DevOps without Scale, Part 2]]></title><description><![CDATA[In Part 1 of this series, I covered why it&#8217;s important to be aware of your product&#8217;s health and how you can do so.]]></description><link>https://jackkora.com/p/devops-without-scale-part-2</link><guid isPermaLink="false">https://jackkora.com/p/devops-without-scale-part-2</guid><dc:creator><![CDATA[Jack Kora]]></dc:creator><pubDate>Thu, 14 Apr 2016 23:01:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!TV7T!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35b2141-999f-4647-b108-ef08a187d1d0_1600x900.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TV7T!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35b2141-999f-4647-b108-ef08a187d1d0_1600x900.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TV7T!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35b2141-999f-4647-b108-ef08a187d1d0_1600x900.jpeg 424w, https://substackcdn.com/image/fetch/$s_!TV7T!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35b2141-999f-4647-b108-ef08a187d1d0_1600x900.jpeg 848w, https://substackcdn.com/image/fetch/$s_!TV7T!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35b2141-999f-4647-b108-ef08a187d1d0_1600x900.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!TV7T!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35b2141-999f-4647-b108-ef08a187d1d0_1600x900.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TV7T!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35b2141-999f-4647-b108-ef08a187d1d0_1600x900.jpeg" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d35b2141-999f-4647-b108-ef08a187d1d0_1600x900.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;DevOps: What You Need To Know&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="DevOps: What You Need To Know" title="DevOps: What You Need To Know" srcset="https://substackcdn.com/image/fetch/$s_!TV7T!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35b2141-999f-4647-b108-ef08a187d1d0_1600x900.jpeg 424w, https://substackcdn.com/image/fetch/$s_!TV7T!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35b2141-999f-4647-b108-ef08a187d1d0_1600x900.jpeg 848w, https://substackcdn.com/image/fetch/$s_!TV7T!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35b2141-999f-4647-b108-ef08a187d1d0_1600x900.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!TV7T!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35b2141-999f-4647-b108-ef08a187d1d0_1600x900.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In <a href="https://jackkora.com/p/devops-without-scale-part-1">Part 1</a> of this series, I covered why it&#8217;s important to be aware of your product&#8217;s health and how you can do so. I covered the user experience and technical types of metrics and how to get started with collecting them. In this part of the series I will walk you through how to use all these metrics to maximize the user experience and product uptime.</p><h2><strong>Metrics, Metrics Everywhere</strong></h2><p>There are no silver bullets here and you have to be aware continuously of your product&#8217;s health and how it fares against user expectations. It surely takes time and effort, but it&#8217;s an essential part of building a good product. Imagine if chair designers didn&#8217;t care for how people use their creations; those chairs would not be comfortable to sit in. Similarly, understanding user needs is a paramount part for any software engineer.</p><p>Once you&#8217;ve established the basic understanding of your application patterns, you are not done. Every release is an opportunity to introduce more latency or failures and the baseline is ever-shifting. It is easy to significantly affect the latency and as easy to break an edge case and drive user failures up by a few percent with any release. The only way to ensure high-quality user experience is to be aware of your product&#8217;s health and your users&#8217; expectations at all times.</p><p>This is the case where less is more and users prefer fewer features that work and are responsive. Try it out and see what your users say!</p><h2><strong>User Experience Monitoring</strong></h2><p>The first part of a good user experience is low error rates. For web products, these include all http error responses as well as all application errors returned to the user. For APIs and GUIs, this includes application failures (and http errors for REST APIs). One recurring comment I hear is that users submit invalid input, which invariably causes error responses and so it should not count. It is certainly useful to be able to differentiate hard failures from bad user input, but the line is more blurred than you think. For example, a new release might change input expectations and previously valid requests can start erroring out. Is that bad user input or did you just break your product?</p><p>In either case, if the failure numbers are high, users won&#8217;t be happy and you need to deal with this problem. The solution here can be non-technical: Maybe you need better documentation or user training, or maybe the API or UX design is cumbersome and drives misunderstanding and misuse. Whatever the case is, it must be addressed. Users are as likely to walk away from a cumbersome product as from a product that doesn&#8217;t work.</p><p>As with everything else, I suggest that you identify a few high-level and most important parts of the product and keep track of those metrics. If you do notice an issue, then you have the more detailed metrics on hand for a deep dive.</p><p>The other part of good user experience is performance, and the approach here is no different. Typically you need to track the latency of the same user actions as you&#8217;re tracking for failures. Performance tracking is where you really need to understand what&#8217;s acceptable and what&#8217;s not for your users and that depends on the domain and your users&#8217; expectations. Two seconds to open a reservation on a public travel site seems like a long time, but two seconds to pull up a loan record for an internal user at a financial company is not bad.</p><p>An important fact to remember is that you will always have some failures in the system and that&#8217;s normal. No matter how great your product is, how clear its documentation, and how in tune you are with your users, there always will be some failures caused by bad user interactions. At some point, once you feel good about your product overall, you will have to assume that the given error rate is your baseline. Then you need to pay attention to variation over time and revisit critically every once in a while.</p><h2><strong>Predictive Monitoring</strong></h2><p>In addition to user experience metrics, there are key technical metrics that are good to watch, as they can point to upcoming problems. Some of the common ones are CPU and memory utilization, thread and database pool sizes, VM heap size and garbage collection times. Just like with user metrics, you need to know the operational signature of your application. But in addition, you need to understand how changes in these metrics impact your users, if at all.</p><p>A growing thread pool might mean that response times are slowing down or it could simply mean that more users are accessing your service. A growing email queue size means that emails aren&#8217;t going out as fast, but is it bad? Only knowing your domain and users will give you the answer. If the CPU is hovering at 80 percent everything is fine-for now, but you need to scale up quickly before a tiny addition of traffic kicks your whole app over.</p><p>When tracking hardware metrics everyone pays close attention to overutilization because that&#8217;s what causes outages. But what about underutilization? Performance fluctuates up and down and at times you can find yourself underutilizing resources. Paying attention to this is a good way to stay frugal and keep costs in check.</p><h2><strong>Alerting</strong></h2><p>How do you actually know when something breaks? You can&#8217;t stare at the graphs all day long. Once you get a feel for the normal characteristics of your system, you&#8217;re ready to create automated monitors and alerts. One of the most important things about automated alerting is that it always must be trustworthy. The moment you and your team start getting false alerts, everyone will learn to disregard some of them and the the value of the system will plummet. Thus I recommend you start with auto-alerting only on several most critical metrics. Run the whole system for a few weeks and slowly add more metrics over time.</p><p>Another aspect to be aware of when setting up automated alerting is metric variations over time, the most common one being day vs. night. Nobody wants to get paged because traffic is too low at 3 a.m. At the same time, if your metrics skyrocket or drop suddenly, it can be a cause for concern. Are you being DDoSed or scraped? Is there a network failure that&#8217;s preventing your service from being reached?</p><p>Tooling to the rescue, <a href="https://www.datadoghq.com/">DataDog</a> has built-in capability to create alarms, called Monitors. They provide a few different ways to ensure no false positives. For example, instead of setting absolute thresholds, you can create a change alert that fires only on deltas. And adding a new monitor only takes a few minutes.</p><p>Finally, someone needs to get paged out when an alert goes off. DataDog can do email notifications but nobody checks that at night and even during core hours it takes time to notice a new email. For critical issues like yours, someone needs to get notified immediately.</p><p>Services like <a href="https://www.pagerduty.com/">PagerDuty</a> and <a href="https://victorops.com/">VictorOps</a> have made it trivial to do this. As expected, DataDog integrates with both of them natively. Both services support on-call rotation scheduling with an escalation policy, calling out to phones, text messaging, emails, and more.</p><h2><strong>Production Troubleshooting</strong></h2><p>So you&#8217;ve been alarmed and need to quickly figure out what&#8217;s going on and what to do. Your next stop is typically logs, but looking through log files one server at a time is a sure way to waste quite a bit of time.</p><p>Log management tools and services have matured over time and there are good alternatives to Splunk. <a href="https://www.loggly.com/">Loggly</a> and <a href="https://logentries.com/">LogEntries</a> cover more than just the basics and provide indexing and search capabilities, graphing of log occurrence, understanding of JSON, and out-of-the-box integration with just about all logging frameworks out there. At their core they provide a quick way to look through all logs without having to go to individual servers. Moreover, you can monitor various aspects of logging &#224; la DataDog and alarm (via VictorOps or PagerDuty) when metrics are off.</p><p>The above approach already solves most needs, but I&#8217;d like to touch on a more advanced problem, especially for a microservice architecture. When multiple services are involved, finding the failure at the entry point is easy but figuring out which back-end service failed in the call chain while processing the user request can be quite difficult.</p><p>An architectural pattern that I call log stitching solves this issue. With log stitching you can pull up all logs across your entire architecture for a given user request. This allows you to reconstruct the complete call chain and quickly find the root cause of failures. Conceptually this technique is straightforward: You generate a unique request ID at the entry point, pass it down the chain for every call, and log it. Now you can pull up all logs with that request ID by searching for it. In practice it can get complicated because you need to: a) ensure consistency across all calls, b) account for all entry points including batch jobs, and c) preserve the request ID internally in multithreaded services.</p><p>Log stitching is not something I recommend from the get-go; however, it&#8217;s definitely something to consider as your system matures. If you are going down the microservices path, this really becomes a must since tracing call chains get progressively more difficult as the number of services grows.</p><h2><strong>Putting it All Together</strong></h2><p>There is not much development work in putting it all together, but nevertheless you will need to put in some time. I&#8217;d like to emphasize how important it is to work in iterations. Set up one to three alarms, connect your DataDog to VictorOps or PageDuty, study them for a couple weeks and then move on. Keep in mind that you will not get it right at first; your thresholds will be off and you will misinterpret or misunderstand system behavior.</p><p>Operational pattern analysis is not trivial, but don&#8217;t spend too much time analyzing the hell out of everything up front. Configure some alarms, get them out there, learn and adjust as you go. When you find an alarm that went off erroneously, take some time to understand why. Specifically, you need to focus on why you thought it was the correct alarm setup vs. why you now know it is not. You will learn by leaps and bounds if you take this systematic approach.</p><p>As I mentioned above, configuring Loggly or LogEntries is super easy and you should do that right away. Log stitching, however, is an involved endeavor that requires planning. Due to its nature it only becomes useful when adopted by all, or most, of your systems. Thus, you should only start it when you feel the need and when you have the buy-in from the rest of your development organization.</p><h2><strong>What&#8217;s Next?</strong></h2><p>Part 3 will cover the topics of continuous integration and continuous delivery, breaking them down into multiple stages of complexity so you can pick and choose what makes the most sense for you from the cost/benefit point of view.</p>]]></content:encoded></item><item><title><![CDATA[DevOps Without Scale, Part 1 ]]></title><description><![CDATA[When most people think of DevOps, what comes to mind first are continuous integration (CI) and continuous development (CD) pipelines.]]></description><link>https://jackkora.com/p/devops-without-scale-part-1</link><guid isPermaLink="false">https://jackkora.com/p/devops-without-scale-part-1</guid><dc:creator><![CDATA[Jack Kora]]></dc:creator><pubDate>Sun, 03 Apr 2016 00:04:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!6gA7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc5eb659-ae74-4b83-9969-14bc7dd8efe2_1600x900.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6gA7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc5eb659-ae74-4b83-9969-14bc7dd8efe2_1600x900.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6gA7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc5eb659-ae74-4b83-9969-14bc7dd8efe2_1600x900.jpeg 424w, https://substackcdn.com/image/fetch/$s_!6gA7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc5eb659-ae74-4b83-9969-14bc7dd8efe2_1600x900.jpeg 848w, https://substackcdn.com/image/fetch/$s_!6gA7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc5eb659-ae74-4b83-9969-14bc7dd8efe2_1600x900.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!6gA7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc5eb659-ae74-4b83-9969-14bc7dd8efe2_1600x900.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6gA7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc5eb659-ae74-4b83-9969-14bc7dd8efe2_1600x900.jpeg" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bc5eb659-ae74-4b83-9969-14bc7dd8efe2_1600x900.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;DevOps: What You Need To Know&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="DevOps: What You Need To Know" title="DevOps: What You Need To Know" srcset="https://substackcdn.com/image/fetch/$s_!6gA7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc5eb659-ae74-4b83-9969-14bc7dd8efe2_1600x900.jpeg 424w, https://substackcdn.com/image/fetch/$s_!6gA7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc5eb659-ae74-4b83-9969-14bc7dd8efe2_1600x900.jpeg 848w, https://substackcdn.com/image/fetch/$s_!6gA7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc5eb659-ae74-4b83-9969-14bc7dd8efe2_1600x900.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!6gA7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc5eb659-ae74-4b83-9969-14bc7dd8efe2_1600x900.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>When most people think of DevOps, what comes to mind first are continuous integration (CI) and continuous development (CD) pipelines. While they are arguably the most important parts of DevOps, they are not all of it. So, then, what is DevOps? It is a philosophy of how to build and operate software products and systems to provide the best user experience. It&#8217;s a way of thinking that blends the previously independent disciplines of development and operations so that software products can both evolve functionally and be stable operationally. In practical terms, it means development teams are involved in operations and, in many instances, fully own the operation of their products.</p><p>The top companies are already there because they otherwise couldn&#8217;t deliver their massive and feature-rich products with 99.9 percent uptime. But there are still too many companies and developers who aren&#8217;t &#8220;doing it.&#8221; And it&#8217;s typically reflected in product quality.</p><p>It seems DevOps has been stigmatized as difficult, requiring expensive tools and generally classified as needed if you&#8217;re big. In this blog I&#8217;ll walk you through different aspects of DevOps and share the business value behind each, the common implementation techniques and the free (or inexpensive) tools that will let you jump-start your DevOps in a matter of weeks.</p><h2><strong>Product Health: The User Experience</strong></h2><p>Our goal as engineers is to build products that will be used by others. Typically we call them users and sometimes we have a strenuous relationship with them. But if users don&#8217;t like our products and aren&#8217;t using them, we have failed at the very core of our job. So what do users want-besides features, that is?</p><p>Users want products that are reasonably fast and work when they need them. Your site going down is an obvious example of why it&#8217;s important to be aware of your product&#8217;s health. But there are more nuanced examples. Sometimes a new release breaks an edge case and isn&#8217;t caught by regression testing. Sometimes a service becomes slow-it still works and nobody is complaining, but it&#8217;s dangerously close to being unusable. You need to be aware of your users&#8217; experience so you can be proactive in fixing problems and delivering quality products.</p><p>Thus, the first set of metrics you need to measure are user experience metrics. These typically include success/failure and latency of all external endpoints: REST, web pages, etc. If the users are external, you want to measure the experience from their point of view, which includes the network latency and HTML/CSS/JS execution time for web pages. If this was a car, these metrics would be your low coolant red light. If the light is on, you must stop as soon as possible and add coolant before your engine block cracks from overheating.</p><p>To measure and monitor customer experience for web pages, <a href="https://www.google.com/analytics/#?modal_active=none">Google Analytics</a> is by far the most popular choice. It&#8217;s easy to set up and gives you exactly what you&#8217;re looking for: error stats per page, page load times, including individual page components and even browser parsing time for HTML. You can slice and dice the data by geographic region, browser type and so on to zero in on improvement or problem areas. Google Analytics is a great tool for tracking other product aspects as well, and you can kill many birds with one stone by adopting it.</p><p>However, Google Analytics will provide data only as long as there are users and your site works. But what if the site breaks, maybe partially or maybe only for a certain geographic region? And what do you do for public APIs that aren&#8217;t instrumentable with Google Analytics? You&#8217;ll need a synthetic monitoring service. These services emulate clients by calling your APIs or opening your web pages in an automated fashion. They are typically scriptable and run from multiple places around the world. They can tell you when your services are up or down and can measure (emulated) user latencies by geographic region. <a href="https://www.pingdom.com/">Pingdom</a> is one of the newer generation of such services that delivers what you need for a modest price. It also integrates with a graphing and alarming tool called <a href="https://www.datadoghq.com/">Datadog</a>, which I&#8217;ll talk about later.</p><h2><strong>Product Health: Technical Innards</strong></h2><p>Now you have a solid understanding of your user experience and can detect user problems in real time. But how do you find the root causes? And can you do even better by detecting user problems before they happen? What you need to do is go a level deeper and monitor key technical aspects of your apps so you can correlate them with user problems. The technical metrics are important to watch because they can be early signs of trouble, which can be averted if you act quickly.</p><p>This can be a dangerous territory as the first desire is to monitor everything. Please keep your cool here, lest you drown yourself in hundreds of metrics that nobody understands and miss the important ones. In software, failure typically happens at integration points, so that&#8217;s what you should focus on: database and cache interactions, calls to other services, disk operations, queue sizes (for pub/sub architectures) and so on. Typically you don&#8217;t need to instrument business logic, as it either works or doesn&#8217;t and problems are caught in testing. This is where it is easy to create many metrics in a blink of an eye. A notable (and rare) exception is load-intensive parts of code that you want to monitor for performance reasons.</p><p>If you see latency in database calls creeping up, cache hit ration going down or thread pool or queue size growing, you can frequently identify and fix the problem before it affects the users. Going even deeper, you want to keep an eye on the virtual machine (VM) and server performance. If your garbage collection times are increasing, if your thread count grows or if your server is showing signs of strain, it&#8217;s time to pay extra attention. If this was a car, these would be your check engine yellow light. If the light is on, you need to check it out soon but can keep on driving for a little bit longer.</p><p>The most popular way to measure application metrics is with code instrumentation. Presently, Coda Hale (a.k.a. <a href="https://dropwizard.github.io/metrics/3.1.0/">Dropwizard</a>) Metrics framework and its ports to most popular languages are the way to go. Given the abundance of documentation and integration with all sorts of systems, this isn&#8217;t difficult. There are a plethora of coding patterns or external libraries you can use to avoid writing boilerplate code. Note that you want to include as much as possible in measuring requests (serializing, logging, etc.) so you need to put this logic as high up the filter chain or middleware as you can.</p><p>The last piece of the puzzle is VM and server monitoring. Traditionally, this has been the territory of specialized ops tools, but Datadog, a service that I will talk about in the next section, provides this out of the box. Its agents run on each machine or VM and collect server metrics, JVM/JMX data, IIS info (yep, it supports even Windows) and more.</p><h2><strong>Processing and Displaying the Data</strong></h2><p>Now your code is instrumented and generates all sorts of interesting data. What do you do with it? And where does the data go in the first place? All this data is useless if it just sits there. You want to be able to look at the trends, correlate them and get a basic set of statistical information such as averages, means, standard deviations and so on. You also want to see pretty graphs (who doesn&#8217;t?) and be able to create dashboards. This is the cool part, where all the previous work comes to fruition.</p><p>Datadog is a great way to start. It provides everything you need and more. All you have to do is install its agent on each box and configure the Metrics framework to send data to Datadog. Most Metrics ports already include Datadog adapters so there is no extra coding required.</p><p>Now you have everything and can slice and dice the data whichever way you want. You can export data for further analysis. You can check it periodically for problems. You can even display dashboards on big screens around the office (looks cool, eh?).</p><h2><strong>Putting it All Together</strong></h2><p>I have seen all of this put together in a couple of weeks. However, if this whole area is new to you it might take longer, as you&#8217;ll be learning the concepts along with the tools. Sometimes the deadlines are tight and even a few weeks is a luxury. However, launching a product without any insight into user experience or product health is not a good idea. If I had limited time and had to prioritize, I&#8217;d start with instrumenting user experience metrics and hooking it up to Datadog. Even for web pages you can start with only monitoring the REST services that power them, or the controller layer, if you&#8217;re on model-view-controller. Soon you will realize how nice it is to have hard metrics and will want to add Google Analytics and deeper code monitoring to your product.</p><p>All of this might seem like a lot, but you&#8217;re only dealing with four tools here: Pingdom, Google Analytics, Metrics framework, and Datadog. If your product is an API then you don&#8217;t need Google Analytics and if your product is internal, you don&#8217;t need Pingdom.</p><p>As I mentioned earlier, Metrics framework is ported to multiple languages already. But even if you have specific requirements that aren&#8217;t supported out of the box, it&#8217;s easy to extend it. For example, at Guaranteed Rate we wrote and open-sourced the <a href="https://github.com/Guaranteed-Rate/App.Lib.MetricsDotNetDatadogPlugin">Metrics/DataDog adapter for .NET</a> and a library for code instrumentation and <a href="https://github.com/Guaranteed-Rate/logdog">DataDog integration for Clojure</a>.</p><p>.NET is particularly known to be behind on DevOps. Until recently one of the reasons was the lack of tools; however, that&#8217;s not the case anymore. All tools mentioned here, as well as Part 2 and 3, work in .NET and have adapters and ports where needed. With .NET entering the new open-source age, there are no more excuses to stay behind.</p><p>Give yourself two weeks, read the docs, go one step at a time, and you&#8217;ll be surprised how far along you can get!</p><h2><strong>What&#8217;s Next?</strong></h2><p>Part 2 will cover what you can do to maximize product uptime based on the data you now have. It also will discuss making sense of the data, automated alerting and troubleshooting when the inevitable production problems happen.</p>]]></content:encoded></item></channel></rss>