{"id":184,"date":"2026-04-09T09:00:00","date_gmt":"2026-04-09T14:00:00","guid":{"rendered":"https:\/\/harmonic-framework.com\/?p=184"},"modified":"2026-04-09T02:36:57","modified_gmt":"2026-04-09T07:36:57","slug":"ebd-in-10-minutes-interface-architecture-that-scales","status":"publish","type":"post","link":"https:\/\/harmonic-framework.com\/es\/ebd-in-10-minutes-interface-architecture-that-scales\/","title":{"rendered":"EBD in 10 Minutes: Interface Architecture That Scales"},"content":{"rendered":"\n<p>Experience-Based Decomposition begins with the same premise as VBD: components should be organized around how they change.<\/p>\n\n\n\n<p>Most frontend patterns organize by size or technical role. <a href=\"https:\/\/atomicdesign.bradfrost.com\/\" rel=\"noopener\">Atomic Design<\/a> gives you atoms, molecules, organisms, templates, and pages \u2014 a clear taxonomy built on composition. It solves a real problem: it stops component libraries from becoming an undifferentiated pile. But it solves it with the wrong axis.<\/p>\n\n\n\n<p>Size is not a volatility axis. An atom changes because a brand standard changed. An organism changes because a business process changed. The two might change together, or never at the same time, and the structure gives you no way to predict which. You end up with a well-organized component library where nobody can confidently scope a feature change.<\/p>\n\n\n\n<p>Experience-Based Decomposition organizes by the reason a component changes, not how large it is.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Axes of Interface Volatility<\/h2>\n\n\n\n<p>In practice, most meaningful frontend change falls into four categories:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Functional volatility<\/strong> \u2014 what the interface enables users to do. Changes when product strategy changes.<\/li>\n<li><strong>Structural volatility<\/strong> \u2014 how the interface sequences a goal. Navigation flow, step ordering, progressive disclosure. Changes when UX architecture changes.<\/li>\n<li><strong>Environmental volatility<\/strong> \u2014 how the interface communicates with the backend. API contracts, data shapes, auth flows. Changes when backend integration changes.<\/li>\n<li><strong>Cross-cutting volatility<\/strong> \u2014 the primitives shared across the interface. Design tokens, form components, loading states. Changes when the design system changes.<\/li>\n<\/ul>\n\n\n\n<p>These axes evolve independently. When they are combined within the same component, change in one axis forces unnecessary churn in the others.<\/p>\n\n\n\n<p>The purpose of decomposition is to prevent that coupling.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Component Tiers<\/h2>\n\n\n\n<p>Experience-Based Decomposition aligns those axes with structural tiers:<\/p>\n\n\n\n<figure class=\"wp-block-table hf-iso-table\"><table><thead><tr><th>Tier<\/th><th>Concern<\/th><th>Responsibility<\/th><\/tr><\/thead><tbody><tr><td>Experience<\/td><td>Functional<\/td><td>Complete user journey, top-level state, backend communication<\/td><\/tr><tr><td>Flow<\/td><td>Structural<\/td><td>Goal-directed sequence within a journey<\/td><\/tr><tr><td>Interaction<\/td><td>Atomic surface<\/td><td>A single atomic user action<\/td><\/tr><tr><td>Utility<\/td><td>Cross-cutting<\/td><td>Shared primitives and design system components<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>An <strong>Experience<\/strong> owns a complete user journey. It knows which Flows are involved, owns the top-level state, and determines what happens in what order. It is also the only tier that communicates with the backend \u2014 when accumulated state is ready to be acted on, the Experience is the one that sends it. It does not render its own UI; it delegates entirely to Flows.<\/p>\n\n\n\n<p>A <strong>Flow<\/strong> owns a goal-directed sequence within an Experience. &#8220;Enter payment details&#8221; is a Flow. &#8220;Review your order&#8221; is a Flow. A Flow knows the Interactions it contains and the local state that connects them. It does not know which Experience it belongs to.<\/p>\n\n\n\n<p>An <strong>Interaction<\/strong> is an atomic user action \u2014 a form field, a confirmation button, an inline validation pattern. It has no knowledge of the Flow it lives in. It exposes a clean interface: inputs and events, nothing more.<\/p>\n\n\n\n<p>A <strong>Utility<\/strong> is a cross-cutting concern \u2014 a date formatter, a design token, an error boundary. It can be used from any tier. It has no business logic and makes no API calls.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Communication Rules<\/h2>\n\n\n\n<p>The effectiveness of this decomposition depends on a single rule:<\/p>\n\n\n\n<p><strong>State flows down, events propagate up, and the Experience is the only tier that communicates with the backend.<\/strong><\/p>\n\n\n\n<p>Experiences delegate to Flows. Flows compose Interactions. Flows surface their accumulated state upward as completion events \u2014 they do not call the backend directly. The Experience holds everything, decides when state is ready to be sent, and decides what happens next.<\/p>\n\n\n\n<p>Flows do not share state laterally. Interactions do not call APIs. Nothing reaches up the tree.<\/p>\n\n\n\n<p>This rule preserves the independence of volatility axes. When a Flow makes a direct API call, a backend contract change begins propagating through the component tree.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">%%{init: {&#039;theme&#039;: &#039;base&#039;, &#039;themeVariables&#039;: {\n  &#039;actorBkg&#039;: &#039;#0b57d0&#039;,\n  &#039;actorTextColor&#039;: &#039;#ffffff&#039;,\n  &#039;actorBorder&#039;: &#039;#0842a0&#039;,\n  &#039;actorLineColor&#039;: &#039;#cfd1d4&#039;,\n  &#039;signalColor&#039;: &#039;#3c4043&#039;,\n  &#039;signalTextColor&#039;: &#039;#3c4043&#039;,\n  &#039;activationBkgColor&#039;: &#039;#e8f0fe&#039;,\n  &#039;activationBorderColor&#039;: &#039;#0b57d0&#039;,\n  &#039;labelBoxBkgColor&#039;: &#039;#f8f9fa&#039;,\n  &#039;labelBoxBorderColor&#039;: &#039;#cfd1d4&#039;,\n  &#039;loopTextColor&#039;: &#039;#3c4043&#039;\n}}}%%\nsequenceDiagram\n    actor U as User\n    participant EXP as CheckoutExperience\n    participant CRF as CartReviewFlow\n    participant PF as PaymentFlow\n    participant CF as ConfirmationFlow\n    participant BE as Backend Workflow\n\n    EXP-&gt;&gt;+CRF: enter(state)\n    U--&gt;&gt;CRF: interacts\n    CRF-&gt;&gt;-EXP: flowComplete(cartState)\n\n    EXP-&gt;&gt;+PF: enter(state + cartState)\n    U--&gt;&gt;PF: interacts\n    PF-&gt;&gt;-EXP: flowComplete(paymentState)\n\n    EXP-&gt;&gt;+CF: enter(accumulated)\n    U--&gt;&gt;CF: interacts\n    CF-&gt;&gt;-EXP: flowComplete(confirmed)\n\n    EXP-&gt;&gt;BE: submit(accumulatedState)<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">What a Well-Engineered Frontend Gets Wrong<\/h2>\n\n\n\n<p>Many teams produce something like this. They are not writing spaghetti components. They are applying <a href=\"https:\/\/medium.com\/@dan_abramov\/smart-and-dumb-components-7ca2f9a7c7d0\" rel=\"noopener\">container\/presentational separation<\/a>. They have custom hooks for data fetching, organisms for layout, molecules for interaction patterns. The structure is principled.<\/p>\n\n\n\n<p>The structure is defensible. This is what careful, experienced work looks like.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">flowchart TB\n    classDef container fill:#0b57d0,color:#ffffff,stroke:#0842a0,stroke-width:2px;\n    classDef organism fill:#fbbc04,color:#202124,stroke:#c49000,stroke-width:2px;\n    classDef hook fill:#188038,color:#ffffff,stroke:#146c2e,stroke-width:2px;\n    classDef external fill:#f8f9fa,color:#3c4043,stroke:#cfd1d4,stroke-width:1.5px;\n\n    CP[&quot;CheckoutPage (Container)&quot;]:::container\n\n    CS[&quot;CartSection (Organism)&quot;]:::organism\n    PS[&quot;PaymentSection (Organism)&quot;]:::organism\n    OS[&quot;OrderSummary (Organism)&quot;]:::organism\n\n    UC[&quot;useCart()&quot;]:::hook\n    UP[&quot;usePayment()&quot;]:::hook\n    UO[&quot;useOrder()&quot;]:::hook\n\n    CART_API[&quot;Cart API&quot;]:::external\n    PAY_API[&quot;Payment API&quot;]:::external\n    ORD_API[&quot;Order API&quot;]:::external\n\n    CP --&gt; CS\n    CP --&gt; PS\n    CP --&gt; OS\n    CP --&gt; UC\n    CP --&gt; UP\n    CP --&gt; UO\n\n    UC --&gt; CART_API\n    UP --&gt; PAY_API\n    UO --&gt; ORD_API<\/code><\/pre>\n\n\n\n<p>Separation of concerns is present. Hooks are reusable. Organisms are composable. This passes review.<\/p>\n\n\n\n<p>And it still has the same fundamental problem.<\/p>\n\n\n\n<p>The hooks are organized by data entity \u2014 <code class=\"\" data-line=\"\">useCart<\/code>, <code class=\"\" data-line=\"\">usePayment<\/code>, <code class=\"\" data-line=\"\">useOrder<\/code>. The container owns step sequencing. The organisms own rendering. When checkout requirements change \u2014 a new step, a conditional flow for guest users, a different step order \u2014 the container logic changes, the hooks may need new data, and the organisms may need to handle new states. The change is functionally local but structurally diffuse.<\/p>\n\n\n\n<p>The hooks are the shared aggregate equivalent. They are organized around data shape, not around what changes them. Everything that needs cart data imports <code class=\"\" data-line=\"\">useCart<\/code>. When the Cart API contract changes, you find out how many places that is.<\/p>\n\n\n\n<p><strong>The abstraction addressed the wrong boundary.<\/strong><\/p>\n\n\n\n<p>Container\/presentational separation asks: what renders and what fetches? Experience-Based Decomposition asks: what forces this to change? These are different questions, and they produce different structures.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Same Interface After Experience-Based Decomposition<\/h2>\n\n\n\n<p>Under Experience-Based Decomposition, the same checkout feature is structured differently.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">flowchart TB\n    classDef experience fill:#0b57d0,color:#ffffff,stroke:#0842a0,stroke-width:2px;\n    classDef flow fill:#fbbc04,color:#202124,stroke:#c49000,stroke-width:2px;\n    classDef interaction fill:#9b59b6,color:#ffffff,stroke:#6c3483,stroke-width:2px;\n    classDef external fill:#f8f9fa,color:#3c4043,stroke:#cfd1d4,stroke-width:1.5px;\n\n    subgraph APP[&quot;Checkout (Experience-Aligned)&quot;]\n        CE[&quot;CheckoutExperience&quot;]\n\n        CRF[&quot;CartReviewFlow&quot;]\n        PF[&quot;PaymentFlow&quot;]\n        CF[&quot;ConfirmationFlow&quot;]\n\n        CII[&quot;CartItemInteraction&quot;]\n        PCI[&quot;PromoCodeInteraction&quot;]\n        CARDI[&quot;CardInputInteraction&quot;]\n        BAI[&quot;BillingAddressInteraction&quot;]\n        OSI[&quot;OrderSummaryInteraction&quot;]\n    end\n\n    CM[&quot;VBD Backend Workflow&quot;]:::external\n\n    CE --&gt; CRF\n    CE --&gt; PF\n    CE --&gt; CF\n\n    CRF --&gt; CII\n    CRF --&gt; PCI\n\n    PF --&gt; CARDI\n    PF --&gt; BAI\n\n    CF --&gt; OSI\n\n    CE --&gt;|accumulated state| CM\n\n    class CE experience\n    class CRF,PF,CF flow\n    class CII,PCI,CARDI,BAI,OSI interaction\n    class CM external<\/code><\/pre>\n\n\n\n<p>The <strong>CheckoutExperience<\/strong> coordinates the journey, owns the top-level state, and is the only component that talks to the backend. It sequences CartReview, then Payment, then Confirmation \u2014 accumulating state from each Flow as it completes.<\/p>\n\n\n\n<p>When the journey is done, it emits that accumulated state as a single coherent event to the corresponding VBD backend workflow \u2014 an OrderManager, a SubmissionManager, whatever the domain calls it. The Experience does not call a cart API, then a payment API, then an order API. It hands off everything at once to the component that owns what happens next.<\/p>\n\n\n\n<p>Each <strong>Flow<\/strong> owns its goal-directed sequence and the local state that connects its Interactions. PaymentFlow knows about card input and billing address. It does not know about cart state, order confirmation, or the backend.<\/p>\n\n\n\n<p>This is the structural isomorphism in action. The Experience and its backend peer change for the same reasons, at the same rate, in response to the same business events \u2014 even if their names reflect different vocabularies. When product says &#8220;change the checkout process,&#8221; the scope is immediately visible: one Experience, one Manager, and whatever Flows and Engines sit beneath them.<\/p>\n\n\n\n<p>Here is what that backend looks like.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">flowchart TB\n    classDef manager fill:#0b57d0,color:#ffffff,stroke:#0842a0,stroke-width:2px;\n    classDef engine fill:#fbbc04,color:#202124,stroke:#c49000,stroke-width:2px;\n    classDef accessor fill:#188038,color:#ffffff,stroke:#146c2e,stroke-width:2px;\n    classDef external fill:#f8f9fa,color:#3c4043,stroke:#cfd1d4,stroke-width:1.5px;\n\n    subgraph APP[&quot;Order Processing (Volatility-Aligned)&quot;]\n        direction TB\n\n        OM[&quot;OrderManager&quot;]\n\n        VE[&quot;ValidationEngine&quot;]\n        PE[&quot;PricingEngine&quot;]\n\n        PM[&quot;PaymentManager&quot;]\n        NM[&quot;NotificationManager&quot;]\n\n        IA[&quot;ItemAccessor&quot;]\n        RA[&quot;RulesAccessor&quot;]\n        PRA[&quot;PriceAccessor&quot;]\n        OA[&quot;OrderAccessor&quot;]\n    end\n\n    subgraph EXT[&quot;External Systems&quot;]\n        direction LR\n        IDS[&quot;Item Data Source&quot;]\n        RDS[&quot;Rules Store&quot;]\n        PDS[&quot;Pricing Data Source&quot;]\n        ODS[&quot;Order Store&quot;]\n        PG[&quot;Payment Provider&quot;]\n        NS[&quot;Notification Service&quot;]\n    end\n\n    OM --&gt; VE\n    VE --&gt; IA\n    VE --&gt; RA\n\n    OM --&gt; PE\n    PE --&gt; PRA\n\n    OM --&gt; OA\n\n    OM -.-&gt;|async| PM\n    OM -.-&gt;|async| NM\n\n    IA --&gt; IDS\n    RA --&gt; RDS\n    PRA --&gt; PDS\n    OA --&gt; ODS\n    PM --&gt; PG\n    NM --&gt; NS\n\n    class OM,PM,NM manager\n    class VE,PE engine\n    class IA,RA,PRA,OA accessor\n    class IDS,RDS,PDS,ODS,PG,NS external<\/code><\/pre>\n\n\n\n<p>The <strong>CheckoutExperience<\/strong> emits accumulated state. The <strong>OrderManager<\/strong> receives it and owns everything from there. Two components, one boundary, clean handoff. Neither needs to know anything about the other&#8217;s internal structure.<\/p>\n\n\n\n<figure class=\"wp-block-pullquote\" style=\"font-size:1rem\"><blockquote><p>The OrderManager might expose a <code class=\"\" data-line=\"\">checkout()<\/code> method \u2014 or it might be <code class=\"\" data-line=\"\">processOrder()<\/code>. The distinction matters. A consumer storefront conceptualizes this as a checkout. A vendor portal conceptualizes it as raising a purchase order. A subscription system conceptualizes it as a renewal. All of them can hand accumulated state to the same OrderManager and let it do its work. The backend doesn&#8217;t know it was a checkout. It doesn&#8217;t need to. Different Experiences, different vocabularies, different user journeys \u2014 same backend workflow. The interface vocabulary and the domain vocabulary are allowed to differ. What must align is the structure.<\/p><\/blockquote><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Change in Practice<\/h2>\n\n\n\n<p>A new requirement arrives: guest users can check out without creating an account.<\/p>\n\n\n\n<p>This is a single, well-scoped requirement. It touches one concern: the journey sequencing. Watch what happens when it lands on each system.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">In the Container\/Hook System<\/h3>\n\n\n\n<p>The <code class=\"\" data-line=\"\">CheckoutPage<\/code> container must branch on authentication state \u2014 it owns sequencing, so it now owns the conditional logic too. The hooks may need to handle unauthenticated API paths. The organisms may need to render differently for guest users. Each piece is a small addition, but they land across the structure.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">flowchart TB\n    classDef changed fill:#d93025,color:#ffffff,stroke:#b31412,stroke-width:2px;\n    classDef affected fill:#e37400,color:#ffffff,stroke:#b06000,stroke-width:2px;\n    classDef unchanged fill:#f1f3f4,color:#9aa0a6,stroke:#dadce0,stroke-width:1px;\n    classDef unchangedExt fill:#f8f9fa,color:#c5c8cb,stroke:#ebebeb,stroke-width:1px;\n\n    CP[&quot;CheckoutPage (Container)&quot;]:::changed\n\n    CS[&quot;CartSection (Organism)&quot;]:::unchanged\n    PS[&quot;PaymentSection (Organism)&quot;]:::affected\n    OS[&quot;OrderSummary (Organism)&quot;]:::unchanged\n\n    UC[&quot;useCart()&quot;]:::unchanged\n    UP[&quot;usePayment()&quot;]:::affected\n    UO[&quot;useOrder()&quot;]:::changed\n\n    CART_API[&quot;Cart API&quot;]:::unchangedExt\n    PAY_API[&quot;Payment API&quot;]:::unchangedExt\n    ORD_API[&quot;Order API&quot;]:::unchangedExt\n\n    CP --&gt; CS\n    CP --&gt; PS\n    CP --&gt; OS\n    CP --&gt; UC\n    CP --&gt; UP\n    CP --&gt; UO\n\n    UC --&gt; CART_API\n    UP --&gt; PAY_API\n    UO --&gt; ORD_API<\/code><\/pre>\n\n\n\n<p><strong>Red<\/strong> \u2014 must change to handle guest state. <strong>Orange<\/strong> \u2014 potentially affected depending on whether guest users follow a different payment or data-fetching path. The change is conceptually simple \u2014 one new journey type \u2014 but structurally invasive because the container owns both sequencing and auth awareness simultaneously.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">In the EBD System<\/h3>\n\n\n\n<p>No new Experience is created. The same <strong>CheckoutExperience<\/strong> gains a <strong>GuestDetailsFlow<\/strong> through configuration \u2014 email capture before payment. The existing Flows are untouched. The Interactions are untouched. Nothing in the existing checkout path changes at all.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">flowchart TB\n    classDef experience fill:#0b57d0,color:#ffffff,stroke:#0842a0,stroke-width:2px;\n    classDef experienceNew fill:#d93025,color:#ffffff,stroke:#b31412,stroke-width:2px;\n    classDef flowNew fill:#d93025,color:#ffffff,stroke:#b31412,stroke-width:2px;\n    classDef flow fill:#f1f3f4,color:#9aa0a6,stroke:#dadce0,stroke-width:1px;\n    classDef interaction fill:#f1f3f4,color:#9aa0a6,stroke:#dadce0,stroke-width:1px;\n    classDef external fill:#f8f9fa,color:#c5c8cb,stroke:#ebebeb,stroke-width:1px;\n\n    CE[&quot;CheckoutExperience&quot;]:::experienceNew\n\n    CRF[&quot;CartReviewFlow&quot;]:::flow\n    PF[&quot;PaymentFlow&quot;]:::flow\n    CF[&quot;ConfirmationFlow&quot;]:::flow\n    GDF[&quot;GuestDetailsFlow&quot;]:::flowNew\n\n    CII[&quot;CartItemInteraction&quot;]:::interaction\n    PCI[&quot;PromoCodeInteraction&quot;]:::interaction\n    CARDI[&quot;CardInputInteraction&quot;]:::interaction\n    BAI[&quot;BillingAddressInteraction&quot;]:::interaction\n    OSI[&quot;OrderSummaryInteraction&quot;]:::interaction\n\n    CM[&quot;VBD Backend Workflow&quot;]:::external\n\n    CE --&gt; CRF\n    CE --&gt; GDF\n    CE --&gt; PF\n    CE --&gt; CF\n\n    CRF --&gt; CII\n    CRF --&gt; PCI\n    PF --&gt; CARDI\n    PF --&gt; BAI\n    CF --&gt; OSI\n\n    CE --&gt;|accumulated state| CM<\/code><\/pre>\n\n\n\n<p>One new component added. The Experience composition changes \u2014 everything else is untouched.<\/p>\n\n\n\n<p>The new journey is a new composition of existing parts. <strong>CartReviewFlow<\/strong>, <strong>PaymentFlow<\/strong>, and <strong>ConfirmationFlow<\/strong> do not change. Every Interaction is unchanged. The backend receives the same accumulated state regardless of which Experience sent it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Same Requirement, Side by Side<\/h3>\n\n\n\n<figure class=\"wp-block-table hf-iso-table\"><table><thead><tr><th>Component<\/th><th>Container\/Hook System<\/th><th>EBD<\/th><\/tr><\/thead><tbody><tr><td>Journey coordination<\/td><td>Changes \u2014 container branches on auth state<\/td><td>Experience composition changes (GuestDetailsFlow added via configuration)<\/td><\/tr><tr><td>Step sequencing<\/td><td>Embedded in container \u2014 changes<\/td><td>Experience adds GuestDetailsFlow to sequence \u2014 existing Flows unchanged<\/td><\/tr><tr><td>Data capture step<\/td><td>New hook and organism section added to container<\/td><td>New GuestDetailsFlow added, isolated<\/td><\/tr><tr><td>Payment path<\/td><td>Potentially affected if guest users have different payment flow<\/td><td>PaymentFlow unchanged \u2014 reused as-is<\/td><\/tr><tr><td>Existing checkout<\/td><td>Modified \u2014 new conditional branches throughout<\/td><td>Untouched \u2014 runs exactly as before<\/td><\/tr><tr><td>Interactions<\/td><td>May need new rendering variants<\/td><td>No change<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<figure class=\"wp-block-pullquote\" style=\"font-size:1rem\"><blockquote><p>If a Flow is just a composition of Interactions with a defined sequence and exit condition, it is already a data structure. A new step is not a code change \u2014 it is a registration. A new journey is not a refactor \u2014 it is a new composition of parts that already exist. The architecture does not just make change cheaper. It changes what change means.<\/p><\/blockquote><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Why This Matters<\/h2>\n\n\n\n<p>Frontend complexity does not usually arrive all at once. It accumulates. A new journey gets added to an existing container. A new API call gets added to an existing hook. A new variant gets conditionally rendered inside an existing organism. Each change is small. The structure quietly degrades.<\/p>\n\n\n\n<p>EBD addresses this at the source. When journeys are Experiences, they compose rather than branch. When goals are Flows, they are reusable across contexts rather than rebuilt per page. When the backend boundary belongs to the Experience, API contract changes have one place to land.<\/p>\n\n\n\n<p>The result is a frontend codebase that product teams can reason about in the same terms engineers use. A new journey is a new Experience. A new step is a new or reordered Flow. The scope of any change is visible before anyone opens a file.<\/p>\n\n\n\n<p>That is the structural property EBD is designed to preserve. Not just clean code \u2014 predictable scope.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Experience-Based Decomposition begins with the same premise as VBD: components should be organized around how they change. Most frontend patterns organize by size or technical role. Atomic Design gives you atoms, molecules, organisms, templates, and pages \u2014 a clear taxonomy built on composition. It solves a real problem: it stops component libraries from becoming an &#8230; <a title=\"EBD in 10 Minutes: Interface Architecture That Scales\" class=\"read-more\" href=\"https:\/\/harmonic-framework.com\/es\/ebd-in-10-minutes-interface-architecture-that-scales\/\" aria-label=\"Read more about EBD in 10 Minutes: Interface Architecture That Scales\">Read more<\/a><\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","footnotes":""},"categories":[3],"tags":[],"methodology":[],"class_list":["post-184","post","type-post","status-publish","format-standard","hentry","category-tutorials"],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false,"trp-custom-language-flag":false,"post-thumbnail":false,"hf-card":false,"hf-hero":false},"uagb_author_info":{"display_name":"William Christopher Anderson","author_link":"https:\/\/harmonic-framework.com\/es\/author\/admin\/"},"uagb_comment_info":0,"uagb_excerpt":"Experience-Based Decomposition begins with the same premise as VBD: components should be organized around how they change. Most frontend patterns organize by size or technical role. Atomic Design gives you atoms, molecules, organisms, templates, and pages \u2014 a clear taxonomy built on composition. It solves a real problem: it stops component libraries from becoming an&hellip;","_links":{"self":[{"href":"https:\/\/harmonic-framework.com\/es\/wp-json\/wp\/v2\/posts\/184","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/harmonic-framework.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/harmonic-framework.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/harmonic-framework.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/harmonic-framework.com\/es\/wp-json\/wp\/v2\/comments?post=184"}],"version-history":[{"count":19,"href":"https:\/\/harmonic-framework.com\/es\/wp-json\/wp\/v2\/posts\/184\/revisions"}],"predecessor-version":[{"id":666,"href":"https:\/\/harmonic-framework.com\/es\/wp-json\/wp\/v2\/posts\/184\/revisions\/666"}],"wp:attachment":[{"href":"https:\/\/harmonic-framework.com\/es\/wp-json\/wp\/v2\/media?parent=184"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/harmonic-framework.com\/es\/wp-json\/wp\/v2\/categories?post=184"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/harmonic-framework.com\/es\/wp-json\/wp\/v2\/tags?post=184"},{"taxonomy":"methodology","embeddable":true,"href":"https:\/\/harmonic-framework.com\/es\/wp-json\/wp\/v2\/methodology?post=184"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}