{"id":22813,"date":"2026-07-27T06:05:08","date_gmt":"2026-07-27T06:05:08","guid":{"rendered":"https:\/\/scannn.com\/two-way-doors-sometimes-lock-behind-you-by-jeff\/"},"modified":"2026-07-27T06:05:08","modified_gmt":"2026-07-27T06:05:08","slug":"two-way-doors-sometimes-lock-behind-you-by-jeff","status":"publish","type":"post","link":"https:\/\/scannn.com\/lv\/two-way-doors-sometimes-lock-behind-you-by-jeff\/","title":{"rendered":"Two-way doors sometimes lock behind you - by Jeff"},"content":{"rendered":"\n<div dir=\"auto\">\n<p><span>You need to make a technical decision between choice A and choice B. Let\u2019s make it for something ubiquitous like feature flags. Someone pipes up with the Bezos style \u201cit\u2019s a <\/span><a href=\"https:\/\/fs.blog\/reversible-irreversible-decisions\/\">reversible decision<\/a><span>\u201d. You convince yourself it\u2019s true and that undoing the decision is low-risk and maybe just a couple of days work. Somewhere there\u2019s an ADR with:<\/span><\/p>\n<blockquote>\n<p><span>Consequences: Low risk. This is a two-way door decision. Feature flags are just booleans behind a function call. If this provider disappoints then swapping back is <\/span><em>just<\/em><span> a couple of days work.<\/span><\/p>\n<\/blockquote>\n<p>That might have been true the day it was written, but it\u2019s probably not true now.<\/p>\n<div class=\"captioned-image-container\">\n<figure><a target=\"_blank\" href=\"https:\/\/substackcdn.com\/image\/fetch\/$s_!acje!,f_auto,q_auto:good,fl_progressive:steep\/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e37eb8-5eab-4955-8347-c90e36b0d080_1024x559.jpeg\" data-component-name=\"Image2ToDOM\" class=\"image-link image2 is-viewable-img can-restack\"><\/p>\n<div class=\"image2-inset\"><picture><source type=\"image\/webp\" srcset=\"https:\/\/substackcdn.com\/image\/fetch\/$s_!acje!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep\/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e37eb8-5eab-4955-8347-c90e36b0d080_1024x559.jpeg 424w, https:\/\/substackcdn.com\/image\/fetch\/$s_!acje!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep\/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e37eb8-5eab-4955-8347-c90e36b0d080_1024x559.jpeg 848w, https:\/\/substackcdn.com\/image\/fetch\/$s_!acje!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep\/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e37eb8-5eab-4955-8347-c90e36b0d080_1024x559.jpeg 1272w, https:\/\/substackcdn.com\/image\/fetch\/$s_!acje!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep\/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e37eb8-5eab-4955-8347-c90e36b0d080_1024x559.jpeg 1456w\" sizes=\"100vw\"\/><\/picture><\/div>\n<p><\/a><figcaption class=\"image-caption\">Gemini: Make a cartoon illustrating this blog post<\/figcaption><\/figure>\n<\/div>\n<p>At first things go well, developers are getting in the habit of using the new feature flag system. But as the system continues to evolve, those flags stop just being feature flags. The product folks have decided to use feature flags as a feature gate. The data science folks are using them for A\/B testing. The homebrew release system has hooked into it. The analytics dashboard reads state from the feature flag system\u2019s API.<\/p>\n<p>Replacing it in the code continues to be simple. Replacing it in the system as a whole now turns out to be much more involved.<\/p>\n<p>A reversible decision is an option you are holding: the right (not the obligation) to switch later when you know more. This is a real asset! However, the mistake is assuming that the asset is durable.<\/p>\n<p>In finance terms, an option is where you pay some money today for the right to do something later, but with no obligation to do so. A call option lets you buy a share at a fixed price at some point in the future. You might execute this option if the price soars, but if the shares tank you don\u2019t have to.<\/p>\n<p>In finance, the strike price is fixed in the contract. The technology argument about options borrows this, but it doesn\u2019t translate. The exercise price of a technical exit is not fixed. It\u2019s set by how you have used the thing since you adopted it, and it can only move in one direction.<\/p>\n<p>Each time a dashboard hooks into the feature flag system, or an engineer learns the query language, then that raises the strike price. You aren\u2019t holding an option with a fixed exercise price. You are holding an option whose strike only ever goes up.<\/p>\n<p>Technical debt compounds through interest you don&#8217;t notice until the invoice arrives. Reversibility debt compounds the same way: the strike price moves every time someone builds on top of the thing, and you don&#8217;t find out how far until you try to exercise. <\/p>\n<p>Maintaining optionality has a cost. If you want to maintain a third-party, then perhaps the cost is the independence layer you write in. Perhaps it means you have to deliberately limit which parts of the platform you adopt to maintain optionality?<\/p>\n<p><span>Either way, you must account for it. If Claude were writing this, it\u2019d be about to tell you that a two-way door decision is <\/span><em>load-bearing.<\/em><span> I won\u2019t do that, I\u2019ll tell you that optionality isn\u2019t a point in time decision, it\u2019s something that must be maintained.<\/span><\/p>\n<p>Once you see this shape of a decision, you\u2019ll start to see it in all the infrastructure services you use.<\/p>\n<p>Cloud services &#8211; They are all the same right? We\u2019ll spin up Docker and just use the standard bits. An easy decision to reverse. Until you start binding to the managed queue services, or the identity aware proxy or the proprietary serverless runtime. Genuine portability survives only if you refuse to touch the opinionated bits, which is the same as refusing the reason you chose to use the platform!<\/p>\n<p><span>CI\/CD servers &#8211; Again, how hard can it be to be portable? It\u2019s just running <\/span><code>.\/build-and-test.sh<\/code><span> right? That\u2019s until you start fanning out runs across multiple machines, configuring permissions and then using the fancy-pants \u201crun only the tests that should be run\u201d features and your teams become dependent on them.<\/span><\/p>\n<p>Observability &#8211; It\u2019s fine; you\u2019re using OTEL. Just change later! Until you\u2019ve got a huge stack of dashboards and alerts written in a language with no portability. Changing your o11y stack now goes well beyond the code.<\/p>\n<ul>\n<li>\n<p><strong>Name the assumptions in your ADR<\/strong><span> &#8211; When you\u2019re making a choice, state what makes it reversible. Going back to our feature flag example, maybe we say that no service may read flag state directly. Sure, that makes it a pain in the ass, but it preserves the optionality.<\/span><\/p>\n<\/li>\n<li>\n<p><strong>Price the option and revisit<\/strong><span> &#8211; You don\u2019t need something exact here, just a number and a direction of travel. Once you\u2019ve got that you can revisit every so often and adjust.<\/span><\/p>\n<\/li>\n<li>\n<p><strong>Test the exit &#8211;<\/strong><span> You might think changing provider is just a case of a data export and reimport, but is it? Prove it!<\/span><\/p>\n<\/li>\n<li>\n<p><strong>Let low-value options expire &#8211;<\/strong><span> Preserving optionality has a cost, and it\u2019s not always worth maintaining. If the choice is a hard to maintain two-way door decision, or a one-way door with known consequences then sometimes it\u2019s worth shutting the door after you (\u201dwe are going all in on XYZ, it\u2019ll become integral to our platform. Leaving XYZ will be a huge effort, but we accept the risk in favour of speed\u201d).<\/span><\/p>\n<\/li>\n<\/ul>\n<p>\u201cWe can always migrate later\u201d is not a static property of the system. Two-way doors don\u2019t magically stay open; they require constant effort.<\/p>\n<p>Don\u2019t pretend they don\u2019t.<\/p>\n<\/div>\n<p><a href=\"https:\/\/fffej.substack.com\/p\/two-way-doors-sometimes-lock-behind?utm_source=tldrnewsletter\">Source link <\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>You need to make a technical decision between choice A and choice B. Let\u2019s make it for something ubiquitous like feature flags. Someone pipes up with the Bezos style \u201cit\u2019s a reversible decision\u201d. You convince yourself it\u2019s true and that undoing the decision is low-risk and maybe just a couple of days work. Somewhere there\u2019s [&hellip;]<\/p>\n","protected":false},"author":16,"featured_media":22814,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[143],"tags":[],"class_list":["post-22813","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai"],"_links":{"self":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/posts\/22813","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/users\/16"}],"replies":[{"embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/comments?post=22813"}],"version-history":[{"count":0,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/posts\/22813\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/media\/22814"}],"wp:attachment":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/media?parent=22813"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/categories?post=22813"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/tags?post=22813"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}