<?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: Aditya Vardhan</title>
    <description>The latest articles on DEV Community by Aditya Vardhan (@adivardh).</description>
    <link>https://dev.to/adivardh</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%2F3265401%2F888bf0cd-5091-4497-8416-67b935880d7e.png</url>
      <title>DEV Community: Aditya Vardhan</title>
      <link>https://dev.to/adivardh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adivardh"/>
    <language>en</language>
    <item>
      <title>When Good OOP Goes Bad: Inheritance vs Composition in Production</title>
      <dc:creator>Aditya Vardhan</dc:creator>
      <pubDate>Sat, 14 Jun 2025 14:43:29 +0000</pubDate>
      <link>https://dev.to/adivardh/when-good-oop-goes-bad-inheritance-vs-composition-in-production-3pd5</link>
      <guid>https://dev.to/adivardh/when-good-oop-goes-bad-inheritance-vs-composition-in-production-3pd5</guid>
      <description>&lt;h1&gt;
  
  
  What I Learned About API Design While Working on a Pydantic Logfire PR
&lt;/h1&gt;

&lt;p&gt;I was working on a PR for Pydantic's Logfire project when I stumbled into something that completely changed how I think about API design. What started as a simple contribution led me down a rabbit hole that taught me how library maintainers actually make decisions in the real world.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Got Here
&lt;/h2&gt;

&lt;p&gt;While digging through the Logfire codebase, I noticed some patterns that seemed connected to OpenTelemetry. Following that thread, I ended up looking at aiohttp's source code and found this interesting discussion about inheritance vs composition.&lt;/p&gt;

&lt;p&gt;The conversation was about discouraging users from inheriting from &lt;code&gt;web.Application&lt;/code&gt; - aiohttp's main class. At first, this seemed backward to me. Isn't inheritance a fundamental part of object-oriented programming?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Users naturally want to extend aiohttp's &lt;code&gt;Application&lt;/code&gt; class like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_custom_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;something useful&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the aiohttp maintainers strongly discourage this. Instead, they want you to use composition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my_app&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_custom_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;something useful&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Here's what I learned about the real reasons behind this design choice:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Collision Problem&lt;/strong&gt;: Imagine you create a method called &lt;code&gt;foo()&lt;/code&gt; in your inherited class. Everything works great. Then, six months later, aiohttp releases a new version that adds a &lt;code&gt;foo&lt;/code&gt; attribute to the base &lt;code&gt;Application&lt;/code&gt; class. Your code breaks in weird, hard-to-debug ways.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Namespace Pollution&lt;/strong&gt;: When you inherit, you're mixing your stuff with the framework's stuff. The maintainer put it perfectly: "it adds user names for base application, application attributes from future versions of aiohttp may clash with user ones."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not Built for It&lt;/strong&gt;: &lt;code&gt;web.Application&lt;/code&gt; wasn't designed as a base class. The framework uses signals and callbacks instead of method overriding patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Trade-offs
&lt;/h2&gt;

&lt;p&gt;The composition approach isn't perfect. You end up with circular references:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;my_app.app&lt;/code&gt; points to the web application&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;app['my_app']&lt;/code&gt; points back to your class&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Type checkers also struggle with &lt;code&gt;app['my_app']&lt;/code&gt; - they don't know what type it is, while inheritance would give you proper typing.&lt;/p&gt;

&lt;p&gt;One user in the discussion pointed out: "don't you find it to be a not-so-good OOP design?" They had a point.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Realized
&lt;/h2&gt;

&lt;p&gt;This conversation showed me that API design isn't about textbook principles. It's about making hard choices between competing priorities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stability&lt;/strong&gt; vs &lt;strong&gt;Convenience&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type Safety&lt;/strong&gt; vs &lt;strong&gt;Flexibility&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean Code&lt;/strong&gt; vs &lt;strong&gt;Future-Proof Code&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The aiohttp maintainers chose stability over convenience. They'd rather have users write slightly more verbose code than risk breaking existing applications when they add new features.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real World is Messy
&lt;/h2&gt;

&lt;p&gt;Before this, I thought good API design meant following OOP principles and making things easy for users. But maintainers have to think about problems I never considered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What happens when we add features in version 2.0?&lt;/li&gt;
&lt;li&gt;How do we avoid breaking thousands of applications?&lt;/li&gt;
&lt;li&gt;What if users depend on implementation details we want to change?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The maintainer even acknowledged: "I understand that the solution is not ideal. Maybe we need to come up with a better solution but it should be not an inheritance."&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Taught Me
&lt;/h2&gt;

&lt;p&gt;Working on that Logfire PR and following this thread taught me that good API design is about predicting the future and making conservative choices. Sometimes the "wrong" solution today prevents bigger problems tomorrow.&lt;/p&gt;

&lt;p&gt;It also made me appreciate the thought that goes into the libraries we use every day. These decisions aren't made lightly - they come from experience dealing with real users and real problems.&lt;/p&gt;

&lt;p&gt;Next time I'm frustrated with a library's design choices, I'll remember this conversation. There's probably a good reason for what seems like an arbitrary decision.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you encountered similar API design decisions that surprised you? I'd love to hear about them in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>python</category>
      <category>api</category>
      <category>aiohttp</category>
    </item>
  </channel>
</rss>
