<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" version="2.0">
  <channel>
    <title>Ali Madooei</title>
    <link>https://madooei.com/</link>
    <atom:link href="https://madooei.com/feed.xml" rel="self" type="application/rss+xml"/>
    <description>Notes on teaching, projects, and ideas.</description>
    <lastBuildDate>Mon, 01 Jun 2026 13:07:08 GMT</lastBuildDate>
    <language>en</language>
    <generator>Lume 3.2.4</generator>
    <author>
      <name>Ali Madooei</name>
    </author>
    <item>
      <title>Generics and Type Erasure in Java</title>
      <link>https://madooei.com/posts/generics-and-type-erasure/</link>
      <guid isPermaLink="false">https://madooei.com/posts/generics-and-type-erasure/</guid>
      <description>
        Why new T[capacity] won't compile, and how one 2004 design decision — type erasure — explains a whole cluster of Java generics gotchas.
      </description>
      <content:encoded>
        <![CDATA[<p>If you've ever written a generic data structure in Java, you've probably run into a line that looks completely reasonable and yet refuses to compile:</p>
<pre><code class="language-java">T[] data = new T[capacity];   // does not compile
</code></pre>
<p>It feels like it should work. You can write <code>new String[capacity]</code> and <code>new int[capacity]</code>, so why not <code>new T[capacity]</code>? The answer is a single design decision Java made back in 2004, and once you understand it, a whole cluster of generic &quot;gotchas&quot; stops being mysterious. That decision is called <em>type erasure</em>. Let's work up to it.</p>
<h2 id="the-generic-array-problem" tabindex="-1"><a href="https://madooei.com/posts/generics-and-type-erasure/#the-generic-array-problem" class="header-anchor">The generic array problem</a></h2>
<p>Say we're building a small generic container, the kind of thing you write once and use with any element type:</p>
<pre><code class="language-java">public class Box&lt;T&gt; {
  private T[] data;

  public Box(int capacity) {
    data = new T[capacity];   // we'd love to write this
  }
}
</code></pre>
<p>The compiler stops us cold on that <code>new T[capacity]</code>. The usual fix is to allocate an array of <code>Object</code> and cast it to <code>T[]</code>:</p>
<pre><code class="language-java">@SuppressWarnings(&quot;unchecked&quot;)
public Box(int capacity) {
  data = (T[]) new Object[capacity];
}
</code></pre>
<p>The cast is <em>unchecked</em>, the compiler can't verify it, so it warns us, and we suppress the warning because <code>data</code> is private and we control every value that goes into it. From the outside, the box behaves exactly as if it held a real <code>T[]</code>.</p>
<p>This is the standard idiom. Java's own <code>ArrayList</code> does exactly this: it stores its elements in an <code>Object[]</code> and casts on the way out. But it raises an obvious question. Why can't we just write <code>new T[capacity]</code> and skip the dance?</p>
<h2 id="type-erasure" tabindex="-1"><a href="https://madooei.com/posts/generics-and-type-erasure/#type-erasure" class="header-anchor">Type erasure</a></h2>
<p>Here's the rule that explains it. <strong>Type erasure</strong> means generic type parameters exist only at compile time. The compiler uses them to check your code, and then it throws them away before your program runs.</p>
<p>So when you write this:</p>
<pre><code class="language-java">Box&lt;String&gt; b = new Box&lt;String&gt;(10);
</code></pre>
<p>the compiler checks every use of <code>b</code> against <code>String</code>, and then erases the type argument. At runtime, what's left behaves as if you'd written:</p>
<pre><code class="language-java">Box b = new Box(10);
</code></pre>
<p>The angle brackets are gone. The running program has no idea that <code>b</code> was ever a &quot;box of <code>String</code>.&quot; And that is precisely why <code>new T[capacity]</code> is illegal: building an array requires knowing its component type <em>at runtime</em>, and by the time the program runs, <code>T</code> has been erased. There's no concrete type left for the array creation to use, so the language disallows the syntax outright rather than let you create something unsound.</p>
<h2 id="what-else-erasure-explains" tabindex="-1"><a href="https://madooei.com/posts/generics-and-type-erasure/#what-else-erasure-explains" class="header-anchor">What else erasure explains</a></h2>
<p>The array restriction is the one people hit first, but it's not alone. Several of Java's generic limitations are the same fact wearing different hats:</p>
<ul>
<li><strong>All instantiations share one class.</strong> <code>Box&lt;String&gt;</code> and <code>Box&lt;Integer&gt;</code> compile to the same bytecode. There is only one <code>Box</code> class at runtime, not one per type argument.</li>
<li><strong><code>instanceof</code> can't ask about a type parameter.</strong> You can't write <code>x instanceof T</code>, and you can't write <code>b instanceof Box&lt;String&gt;</code>. The type argument isn't there at runtime to test against, so the check is meaningless and the compiler rejects it.</li>
<li><strong>You can't do <code>new T()</code> either</strong>, for the same reason <code>new T[]</code> fails: there's no concrete type to construct.</li>
</ul>
<p>None of these are arbitrary rules someone added to make your life harder. They all fall out of one decision: the type argument is not around at runtime to act on.</p>
<h2 id="why-java-erases" tabindex="-1"><a href="https://madooei.com/posts/generics-and-type-erasure/#why-java-erases" class="header-anchor">Why Java erases</a></h2>
<p>Other languages made different choices. C# keeps generic type information at runtime; C++ templates generate a separate concrete class for each type you use. So why did Java erase?</p>
<p>Because of history. Generics arrived in Java 5, in 2004, nearly a decade after the language shipped. By then there was already an enormous body of pre-generic code in the wild, code full of raw types like <code>List</code> and <code>ArrayList</code> with no angle brackets at all. The language designers wanted new generic code and old raw code to keep working together, in the same program, without a painful migration. Erasure made that possible: because <code>Box&lt;String&gt;</code> and a raw <code>Box</code> are the same type underneath, the new and the old interoperate seamlessly.</p>
<p>The limitations we walked through, the array problem, the missing runtime type, the broken <code>instanceof</code>, are the price Java paid for that backward compatibility. Whether it was the right call is a fun thing to argue about. But knowing <em>that</em> it was the call, and why, turns a pile of confusing compiler errors into a single idea you can reason about.</p>
<p>So the next time <code>new T[capacity]</code> won't compile, you'll know it isn't the compiler being difficult. It's just type erasure, doing exactly what it was designed to do.</p>
]]>
      </content:encoded>
      <pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate>
    </item>
    <item>
      <title>On Full-Stack Development in the Age of AI</title>
      <link>https://madooei.com/posts/on-fullstack-and-ai/</link>
      <guid isPermaLink="false">https://madooei.com/posts/on-fullstack-and-ai/</guid>
      <description>
        Should you still learn full-stack development when AI can generate applications? An honest assessment of the changing landscape for junior developers.
      </description>
      <content:encoded>
        <![CDATA[<p>A student recently asked me a question I'm sure many others are wondering about:</p>
<blockquote>
<p>How important is full stack development given that AI coding tools can now generate full stack applications fairly reliably? If it's still worth pursuing, which stack would you recommend learning?</p>
</blockquote>
<p>It's a fair question. I teach a course called Full-Stack JavaScript, and I'd be concerned too if I were a student watching AI agents generate complete applications. Let me give you an honest answer, even if it's not entirely reassuring.</p>
<p>The short version: Yes, I still think learning full-stack development is valuable. But the landscape is changing in ways that are uncomfortable to acknowledge.</p>
<h2 id="why-this-course-exists" tabindex="-1"><a href="https://madooei.com/posts/on-fullstack-and-ai/#why-this-course-exists" class="header-anchor">Why This Course Exists</a></h2>
<p>First, let's be clear about something. I didn't create Full-Stack JavaScript out of love for JavaScript or passion for full-stack development. I created it to help students be job ready. That was my sole intention—to give you the skills needed to work as software developers, particularly building applications that businesses actually need.</p>
<p>The question is whether that preparation still makes sense when AI can do much of what we teach in that course.</p>
<h2 id="the-complexity-spectrum" tabindex="-1"><a href="https://madooei.com/posts/on-fullstack-and-ai/#the-complexity-spectrum" class="header-anchor">The Complexity Spectrum</a></h2>
<p>To understand where AI fits, think about software projects along multiple axes of difficulty:</p>
<ul>
<li><strong>Problem familiarity</strong>: conventional → novel</li>
<li><strong>Algorithmic complexity</strong>: low → high</li>
<li><strong>Specification clarity</strong>: precise → ambiguous</li>
<li><strong>Architectural scope</strong>: localized → distributed</li>
<li><strong>External integrations</strong>: none → many</li>
<li><strong>Environment complexity</strong>: simple → complex (setup, build, deployment)</li>
<li><strong>State and data complexity</strong>: stateless → heavy state management</li>
<li><strong>Concurrency requirements</strong>: single-threaded → highly concurrent</li>
<li><strong>Non-functional constraints</strong>: minimal → strict (performance, security, compliance)</li>
<li><strong>Robustness requirements</strong>: basic → mission-critical</li>
<li><strong>Tool/API volatility</strong>: stable → rapidly changing</li>
<li><strong>UI/UX complexity</strong>: simple forms → complex flows</li>
<li><strong>Backward compatibility</strong>: greenfield → legacy integration</li>
<li>And more...</li>
</ul>
<p>The simpler a project is on each axis, the better AI coding agents do. As complexity rises along any of these dimensions, they struggle more, the same way a human developer would.</p>
<h2 id="where-ai-stands-today" tabindex="-1"><a href="https://madooei.com/posts/on-fullstack-and-ai/#where-ai-stands-today" class="header-anchor">Where AI Stands Today</a></h2>
<p>The hard part to admit is this: AI coding agents are excellent at the simpler-to-moderate levels of this spectrum. They can do very well what a junior software developer can do. Everything I teach in Full-Stack JavaScript—setting up servers, creating REST APIs, building React frontends, connecting to databases, deploying applications—AI can do with flying colors, much better than students just learning these skills. (And these tools keep getting better, with no sign of slowing down.)</p>
<p>But it hasn't bridged the gap to become completely autonomous. It's not yet at the &quot;build me X and it shall be&quot; stage. You still need a human co-pilot to guide it, validate its decisions, catch its mistakes, and handle the ambiguous parts.</p>
<p>Current evidence suggests that AI copilots work best with senior developers. Senior + AI is more productive than Senior + Junior. This creates an obvious incentive: replace junior developers with AI.</p>
<h2 id="the-market-reality" tabindex="-1"><a href="https://madooei.com/posts/on-fullstack-and-ai/#the-market-reality" class="header-anchor">The Market Reality</a></h2>
<p>This isn't speculation. The data (see [1] and [2]) is already showing the shift:</p>
<p>Junior roles are shrinking. Studies across 250,000 firms show that headcount for early-career software developers (ages 22-25) has been dropping steadily, right as AI coding tools spread.</p>
<p>Senior roles are growing. Headcount for developers aged 31 and up keeps expanding, and companies struggle to find enough senior engineers, poaching talent or buying whole companies just to secure capable developers.</p>
<p>And firms are cutting junior hiring on purpose. They're reducing the roles they expect to automate, because they see it as cheaper than future layoffs: cut the hiring now rather than lay people off later.</p>
<h2 id="the-paradox" tabindex="-1"><a href="https://madooei.com/posts/on-fullstack-and-ai/#the-paradox" class="header-anchor">The Paradox</a></h2>
<p>Here's the problem: you can't create a course that directly turns someone into a senior developer. Senior developers are made through years of experience, starting as juniors and growing through mistakes, projects, and mentorship.</p>
<p>This creates two possible futures:</p>
<p><strong>Scenario 1</strong>: You start learning now, but before you develop senior-level skills, AI closes the gap. It becomes fully autonomous, handles complex projects, and junior-to-mid-level developers are no longer needed. You're out of luck.</p>
<p><strong>Scenario 2</strong>: You start learning now, but AI fails to close the gap. The industry suffers from its reduction in junior roles. As current seniors retire, there aren't enough people to replace them because we stopped training juniors. You become extremely valuable.</p>
<p>I hope the second scenario happens. Unfortunately, major companies seem to be betting on the first.</p>
<h2 id="so-should-you-take-the-course" tabindex="-1"><a href="https://madooei.com/posts/on-fullstack-and-ai/#so-should-you-take-the-course" class="header-anchor">So Should You Take the Course?</a></h2>
<p>Despite all this, my answer is still yes—but with clear eyes about what you're getting into.</p>
<p>You need to understand the fundamentals. Even if AI is writing the code, someone has to know whether that code makes sense, whether the architecture is sound, whether the security implications have been considered. You can't guide an AI copilot well if you don't understand what you're asking it to build.</p>
<p>The complexity spectrum matters here. The full-stack development we teach sits at the simpler end on purpose, but you need those fundamentals before you can tackle the complex problems AI still can't handle on its own. You have to crawl before you walk.</p>
<p>Speed matters. If you're going to beat the AI gap, you can't take the leisurely four-year path. Learn intensively, build real projects, contribute to meaningful work, and move from junior to mid-level as fast as you can. The clock is ticking.</p>
<p>Depth still beats breadth. Don't try to learn every framework and stack. Pick one, learn it deeply, understand the principles, and get good at solving real problems. The ability to think through hard technical challenges is what will set you apart from AI. (Think about that the next time you're signing up for another humanities course!)</p>
<h2 id="what-about-which-stack" tabindex="-1"><a href="https://madooei.com/posts/on-fullstack-and-ai/#what-about-which-stack" class="header-anchor">What About Which Stack?</a></h2>
<p>You might be tempted by another angle: since AI coding agents are better at popular stacks (trained on more data, better documentation, more Stack Overflow answers), maybe you should learn something esoteric to gain an edge. This is a mistake. You're trying to compete with AI, and you can't win that game. AI will eventually cover the esoteric stacks too, and you'll have spent your time learning technologies with smaller job markets and fewer resources. You'll have optimized for being different from AI rather than being good at building software.</p>
<p>Put AI aside. Don't make your technology choices based on what AI can or can't do. Make them based on what you need to learn to build real applications and get real jobs. If you want to build web applications, learn JavaScript and its popular frameworks and libraries—that's where the jobs are. If you want to work on machine learning or data science, learn Python and its ecosystem. Pick something practical with real market demand, go deep, and build things that matter.</p>
<p>Whatever stack or domain you choose, learn to use AI well within it. The future isn't about competing with AI; it's about working alongside it. Learn the fundamentals deeply enough that you can guide AI tools, validate their output, and catch their mistakes. Use AI to accelerate your development, not to replace your understanding. The developers who thrive won't be the ones who avoid AI or try to outrun it; they'll be the ones who understand their craft well enough to make AI a force multiplier.</p>
<p>There's a catch, though: using AI while you're still learning can get in the way of that learning if you're not careful. There's a real tension between learning to use AI and using AI to learn. I'll write more about how to navigate this in a future post, but for now, be mindful of the difference between using AI as a tutor and using it as a ghost writer.</p>
<h2 id="the-honest-bottom-line" tabindex="-1"><a href="https://madooei.com/posts/on-fullstack-and-ai/#the-honest-bottom-line" class="header-anchor">The Honest Bottom Line</a></h2>
<p>I can't promise that learning full-stack development will help you get a job in five years. The industry is changing too rapidly, and the AI capabilities are advancing too quickly for anyone to make that promise honestly.</p>
<p>What I can say is this: understanding how to build software systems, even if AI is doing the typing, remains valuable. The question is whether that value translates to employment opportunities at the junior level, or whether you'll need to accelerate past that tier before the market for junior developers disappears entirely.</p>
<p>It's a gamble. But it's a more informed gamble than going in blind.</p>
<p>If you're going to make this bet, make it count. Don't just complete the assignments. Build real things. Contribute to open source. Work on projects that challenge you. Aim to be mid-level capable by the time you graduate, not entry-level.</p>
<p>The industry is changing. How you adapt to that change will matter more than which stack you choose to learn.</p>
<hr>
<p><strong>References:</strong></p>
<ul>
<li>[1] Hosseini Maasoum, Seyed Mahdi and Hosseini Maasoum, Seyed Mahdi and Lichtinger, Guy, Generative AI as Seniority-Biased Technological Change: Evidence from U.S. Résumé and Job Posting Data (August 31, 2025). Available at SSRN: https://ssrn.com/abstract=5425555 or http://dx.doi.org/10.2139/ssrn.5425555</li>
<li>[2] Brynjolfsson, Erik, Bharat Chandar, and Ruyu Chen. &quot;Canaries in the coal mine? six facts about the recent employment effects of artificial intelligence.&quot; Stanford Digital Economy Lab. Published August (2025). Available at: https://digitaleconomy.stanford.edu/wp-content/uploads/2025/08/Canaries_BrynjolfssonChandarChen.pdf</li>
</ul>
]]>
      </content:encoded>
      <pubDate>Thu, 13 Nov 2025 00:00:00 GMT</pubDate>
    </item>
    <item>
      <title>On Course Load and Learning: Why Less is More</title>
      <link>https://madooei.com/posts/on-course-load-and-learning/</link>
      <guid isPermaLink="false">https://madooei.com/posts/on-course-load-and-learning/</guid>
      <description>
        An argument for taking fewer courses, pursuing depth over breadth, and choosing challenge over convenience in undergraduate education.
      </description>
      <content:encoded>
        <![CDATA[<p>If you're like many smart, motivated students, you stack a heavy course load, add extracurriculars, and end up overwhelmed—still feeling you haven't done enough. Your semester can feel like chasing a train that never stops long enough to board. The intent is to make the most of your undergraduate years; the result is burnout, superficial learning, and missed opportunities for genuine intellectual growth.</p>
<p>My advice is simple: take fewer courses. Focus on depth over breadth. Choose challenge over convenience. For most undergraduates, fewer courses done thoroughly is the wiser default.</p>
<p>Graduating early: Taking more courses can let you finish in three years and save on tuition and housing. If you face serious financial constraints, consider this path. For most students who can afford the standard four years, the depth and time for advanced work outweigh the cost savings; graduating early often means sacrificing depth of learning—the real value of your undergraduate education.</p>
<p>If you can afford the full four years (for example, because you're on a full scholarship), you may still feel pressure to overload on courses and extracurriculars. There is a pervasive belief that more is better—that piling on courses, majors, and activities will lead to a richer experience and more opportunities after graduation. After all, whether you take 12 credits or 21+ credits, you still pay the same tuition. Why not get the most out of it? Course registration can start to resemble an all‑you‑can‑eat buffet. That's where the logic falters. Here's the case for less being more.</p>
<h2 id="the-math-doesn-t-work" tabindex="-1"><a href="https://madooei.com/posts/on-course-load-and-learning/#the-math-doesn-t-work" class="header-anchor">The Math Doesn't Work</a></h2>
<p>Let's start with basic arithmetic. The federal definition of a credit hour is this: one hour of classroom instruction plus a <em>minimum</em> of two hours of out-of-class work per week equals one credit hour. That's three hours total per credit, per week (in a typical semester), as a minimum. So, for example, if you're taking a 3-credit course, you should expect to spend at least 9 hours per week on that course (3 hours in class + 6 hours studying at home). There are exceptions, of course! For example, a lab course may require much less outside work, while a project-based course may require much more. But as a general rule of thumb, this is the standard.</p>
<p>A full-time student is one whose primary occupation is studying. Full-time employment is typically considered to be around 40 hours per week. Therefore, a full-time student should be dedicating approximately 40 hours per week to their studies. At 12 credits (the minimum for full-time status at Hopkins), that's 36 hours per week. At 15 credits (a typical course load of 3–4 courses), you're at 45 hours—already above the 40-hour mark.</p>
<p>If you take 18–21 credits, that's 54–63 hours per week just for coursework. Now add in extracurriculars (research projects, student clubs, applying for internships, etc.), personal responsibilities, and perhaps a part-time job. It becomes unsustainable. Something has to give, and what gives is depth of learning.</p>
<p>While it's true that some courses require less work, that's usually at the expense of rigor and depth. An easy or superficial course can help you check a box, satisfy a requirement, or boost your GPA, but it won't grow you intellectually the way a hard one will. And that growth is the point of an undergraduate education, especially in engineering. Paying tuition for an easy A that amounts to busywork, while skipping the advanced courses that demand real effort (the ones you're unlikely to study on your own, outside the scaffolding of a class), is a poor trade when time is zero-sum. I'd take the B+ in a rigorous course over the easy A.</p>
<p>The credit-hour math is a minimum. In practice, most technical courses routinely exceed it, so you'll likely spend more time than that. And even if you push well past 50–60 hours a week, there's a ceiling on how much a person can absorb: the brain has finite attention and processing capacity, and piling on more courses past that point just lowers the quality of what you learn.</p>
<h2 id="the-multiple-major-trap" tabindex="-1"><a href="https://madooei.com/posts/on-course-load-and-learning/#the-multiple-major-trap" class="header-anchor">The Multiple Major Trap</a></h2>
<p>It's common to see students major in two or more fields, often adding a minor or two for good measure. In principle, in fields with shared cores (e.g., CS + Applied Math), a second major or minor can formalize complementary depth and perhaps open doors for interdisciplinary work. In practice, however, when you commit to multiple majors within a fixed timeframe (typically four years and within a fixed credit cap), each additional major forces you to optimize for breadth over depth. Your learning becomes a series of requirements to satisfy rather than a coherent intellectual journey. You take the courses you <em>must</em> take, not necessarily the ones you <em>should</em> take.</p>
<p>What's worse is that many programs have adjusted their requirements to make double majors feasible. As multiple majors become common, the marginal signaling value of an additional major declines. What stands out beyond credential inflation is actual evidence of mastery: a thesis, research paper, open-source contributions, or performance in advanced electives—outcomes that require slack in the schedule.</p>
<h2 id="the-extracurricular-paradox" tabindex="-1"><a href="https://madooei.com/posts/on-course-load-and-learning/#the-extracurricular-paradox" class="header-anchor">The Extracurricular Paradox</a></h2>
<p>On top of an overloaded course schedule or multiple majors, you may try to maintain research positions in labs, participate in independent studies, lead clubs, and engage in various other activities—all simultaneously. The intent is admirable. The execution is often superficial.</p>
<p>When you spread yourself this thin, you can't give any single activity the attention it deserves. You do the bare minimum to put a checkmark next to each item on your resume. You're present, but you're not really engaged. You're participating, but you're not making meaningful contributions. Concentrated effort on fewer, harder things yields artifacts, references, and real competence.</p>
<h2 id="a-better-path" tabindex="-1"><a href="https://madooei.com/posts/on-course-load-and-learning/#a-better-path" class="header-anchor">A Better Path</a></h2>
<p>Here's what I recommend instead.</p>
<p>Take three or four courses a semester, and make them count. This fits both the federal credit-hour definition and the reality of deep learning. It gives you time to actually understand the material, to struggle with hard concepts, to go past the minimum.</p>
<p>Commit to a single major. Take the advanced courses. Take the ones that scare you a little. Build real expertise in your field. You can always take electives outside your major to explore other interests, without the pressure of satisfying a second major's requirements. No one ever got hired because they had two majors; they got hired because they were good at what they did.</p>
<p>Use summers for extracurriculars. If you want to do research, an independent study, or anything else, do it over the summer when you're not taking courses. Give each one the attention it deserves instead of spreading yourself thin. Take on one or two meaningful things across your whole degree, and stick with them rather than dabbling in many.</p>
<p>Choose challenge over convenience. Take the courses you wouldn't study on your own, where you'll learn from the professor's expertise and your classmates' perspectives. Take the ones that matter for the field you care about and genuinely challenge you. Avoid the &quot;easy A&quot; that doesn't stretch you. The goal is to grow, not to collect a good grade.</p>
<p>Plan for four years. Unless you have serious financial constraints, there's no prize for finishing early, especially if finishing early means you learned less. Many students who rush through in three years end up adding a master's year anyway. Slow down, take your time, and make it count.</p>
<h2 id="the-real-value" tabindex="-1"><a href="https://madooei.com/posts/on-course-load-and-learning/#the-real-value" class="header-anchor">The Real Value</a></h2>
<p>The value of higher education isn't in the number of degrees you collect, the majors and minors you accumulate, the courses you cram into each semester, or the activities you juggle. It's in the transformation that happens when you engage deeply with challenging material—when you have time to think, question, and connect ideas across domains. That transformation, not the transcript, is what will serve you for the next forty years of your career.</p>
<p>That's what I want for my students. Not more courses, more majors, more activities—but deeper learning, genuine challenge, and the time and space to become truly competent in your field.</p>
<p>Sometimes less really is more.</p>
]]>
      </content:encoded>
      <pubDate>Sun, 09 Nov 2025 00:00:00 GMT</pubDate>
    </item>
    <item>
      <title>Welcome</title>
      <link>https://madooei.com/posts/welcome/</link>
      <guid isPermaLink="false">https://madooei.com/posts/welcome/</guid>
      <description>A short note about why this site exists.</description>
      <content:encoded>
        <![CDATA[<p>This is the first post on the new site. The unit of content here is just a
<strong>post</strong> — sometimes that's a course page, sometimes a project log, sometimes
an opinion. Posts can be grouped together when they belong in a sequence (a
multi-part tutorial, a course's lectures), but the default is flat.</p>
<p>If you want a feed, <a href="https://madooei.com/feed.xml">here it is</a>.</p>
]]>
      </content:encoded>
      <pubDate>Wed, 30 Apr 2025 00:00:00 GMT</pubDate>
    </item>
  </channel>
</rss>