<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mike Georgeff</title>
    <description>The latest articles on DEV Community by Mike Georgeff (@georgeff).</description>
    <link>https://dev.to/georgeff</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3965697%2F8201ca64-8b55-4d61-8104-865808572585.jpeg</url>
      <title>DEV Community: Mike Georgeff</title>
      <link>https://dev.to/georgeff</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/georgeff"/>
    <language>en</language>
    <item>
      <title>Two Phases, Two APIs</title>
      <dc:creator>Mike Georgeff</dc:creator>
      <pubDate>Tue, 09 Jun 2026 03:01:24 +0000</pubDate>
      <link>https://dev.to/georgeff/two-phases-two-apis-425j</link>
      <guid>https://dev.to/georgeff/two-phases-two-apis-425j</guid>
      <description>&lt;p&gt;Most applications have no enforced lifecycle boundaries. Services get resolved at arbitrary times, configs mutate mid-request, init logic bleeds into request handling. The result is an application that is difficult to reason about because its state is never truly settled.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Two Phases
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Boot&lt;/strong&gt;: The period before the application handles any work. The sole purpose of the boot phase is construction, registering service definitions, merging configuration, wiring dependencies, and running initialization logic. When boot ends, the service layer is immutable, nothing new can be registered, nothing can be reconfigured.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run&lt;/strong&gt;: The application transforms inputs into outputs using the fully constructed service layer. No reconfiguration, no new definitions, no structural mutations, only pure transformation.&lt;/p&gt;

&lt;p&gt;The hard boundary between boot and run is not a convention, it is a constraint.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the Boundary Matters
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Predictability&lt;/strong&gt;: if the service layer is immutable after boot you always know what you are working with during a request&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testability&lt;/strong&gt;: an immutable service graph is easy to inspect and reproduce in tests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debuggability&lt;/strong&gt;: boot-time errors fail loud and early, they do not surface as mysterious runtime behavior&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: a service layer that cannot be mutated at runtime prevents the injection of rogue providers or configuration overrides, shrinking the attack surface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Boot is boot, run is run, this should be enforced, not suggested.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Two APIs
&lt;/h3&gt;

&lt;p&gt;The package &lt;a href="https://github.com/MikeGeorgeff/kernel" rel="noopener noreferrer"&gt;&lt;code&gt;georgeff/kernel&lt;/code&gt;&lt;/a&gt; enforces this at the type level.&lt;/p&gt;

&lt;p&gt;When correctly composed, kernel definitions are registered before boot is called, and the fully initialized container is available after.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$kernel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Kernel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$kernel&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addDefinition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MyClass&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nv"&gt;$kernel&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;boot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$kernel&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getContainer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MyClass&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;addDefinition&lt;/code&gt; is called after boot, a &lt;code&gt;KernelException&lt;/code&gt; will be thrown because the service layer is immutable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$kernel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Kernel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$kernel&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;boot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$kernel&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addDefinition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MyClass&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the container is accessed before boot, a &lt;code&gt;KernelException&lt;/code&gt; will be thrown because the container has not yet been initialized.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$kernel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Kernel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$kernel&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getContainer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$kernel&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;boot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run begins the moment &lt;code&gt;boot()&lt;/code&gt; returns, after the kernel is a resolved, immutable service graph. The handoff from construction to transformation is complete.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modules as Boot-Time Citizens
&lt;/h3&gt;

&lt;p&gt;In the &lt;code&gt;georgeff/kernel&lt;/code&gt; package, modules enforce the phase separation by design. &lt;code&gt;ModuleInterface::register()&lt;/code&gt; is a boot-time method, whose purpose is to register service definitions, tags, and decorators. &lt;code&gt;BootableModuleInterface::boot()&lt;/code&gt; is called at the tail end of the boot cycle, providing access to the built container for initialization work. New definitions cannot be registered at this stage since the service graph is already sealed. Neither has any role in run, making it structurally impossible to blur the line.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phases as Architecture
&lt;/h3&gt;

&lt;p&gt;The boot/run distinction is not a pattern unique to this kernel, it is a general principle that most applications implement accidentally and inconsistently. Making it explicit, enforcing it with hard constraints, and designing your application around it produces software that is honest about its own state.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Related&lt;/strong&gt;: &lt;a href="https://dev.to/georgeff/natural-aristoi-h6j"&gt;Natural Aristoi&lt;/a&gt; — the philosophy behind the design decisions in this package.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>softwareengineering</category>
      <category>programming</category>
    </item>
    <item>
      <title>Natural Aristoi</title>
      <dc:creator>Mike Georgeff</dc:creator>
      <pubDate>Wed, 03 Jun 2026 05:08:40 +0000</pubDate>
      <link>https://dev.to/georgeff/natural-aristoi-h6j</link>
      <guid>https://dev.to/georgeff/natural-aristoi-h6j</guid>
      <description>&lt;p&gt;Frameworks should earn their place in your application through demonstrated merit -- not convention, not network effect, not being the path of least resistance.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Concept
&lt;/h3&gt;

&lt;p&gt;Natural Aristoi is a concept championed by Thomas Jefferson describing a governing class defined by virtue and talent, not birth or privilege. Jefferson believed in small government, states' rights over federal, power closest to the people it governs -- local, visible, accountable. Distrust of centralized authority that accumulates complexity, imposes its will and becomes impossible to remove once entrenched.&lt;/p&gt;

&lt;p&gt;Small codebase. Components over monolith. Architecture closest to the problem it solves -- visible, intentional, accountable in its behavior. Distrust of the monolithic framework that grows, mandates, and makes decisions on your behalf whether you asked it or not.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem with Full Frameworks
&lt;/h3&gt;

&lt;p&gt;Frameworks force an all-or-nothing decision. Opinions are baked into every layer. This leads to abstractions that you did not ask for that hide what is actually happening. When your needs diverge, you're fighting the framework instead of engineering the solution.&lt;/p&gt;

&lt;p&gt;Ultimately engineering comes in second, the framework's conventions come first.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Composability Alternative
&lt;/h3&gt;

&lt;p&gt;Compose only what your application needs -- nothing more, nothing less. Each component earns its place by doing precisely one thing. Shared standards serve as the contract layer -- components speak to each other through interfaces, not implementations. In PHP, PSR standards serve this exact role -- defining interfaces that components agree on without mandating how any of them are implemented. The result is small, purposeful, replaceable pieces that assemble into something cohesive.&lt;/p&gt;

&lt;p&gt;The engineer decides the architecture, not the framework.&lt;/p&gt;

&lt;h3&gt;
  
  
  Principles
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Define hard boundaries&lt;/strong&gt; -- boot is boot, run is run, this should be enforced, not suggested.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immutability as a constraint&lt;/strong&gt; -- the service layer freezes at boot, making predictability a guarantee.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit over implicit&lt;/strong&gt; -- dependencies are declared, not located; registration is separated from resolution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pure transformation&lt;/strong&gt; -- the system transforms inputs, it does not reconfigure itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restraint as a signal&lt;/strong&gt; -- 165 lines for a DI container, because that's all it needs to be.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why this Matters
&lt;/h3&gt;

&lt;p&gt;Applications built this way are predictable, testable, and honest about their own state. Implementation changes do not cause a snowball effect, they are centralized and isolated. Good software earns its place, flawed software is easily removed.&lt;/p&gt;

&lt;p&gt;Engineers who understand their stack make better decisions.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>softwareengineering</category>
      <category>philosophy</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
