Flutter Anti-Pattern: How setState() Turns Your App Into a Slideshow

Flutter Anti-Pattern

Flutter Anti-Pattern

The Problem: Many Flutter developers overuse setState(), calling it even when variable changes don’t affect the UI. Every unnecessary setState() is a potential drop from 60 FPS to 59 FPS in complex interfaces.

Chapter 1: When setState Is Actually Unnecessary

Let’s examine a classic example found in 80% of applications: data entry forms. Developers often write:

class MyForm extends StatefulWidget {
  @override
  _MyFormState createState() => _MyFormState();
}

class _MyFormState extends State<MyForm> {
  String _username = '';
  String _password = '';

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextField(
          onChanged: (value) {
            setState(() {          // ❌ UNNECESSARY!
              _username = value;   // This variable is NOT used in UI
            });
          },
        ),
        TextField(
          onChanged: (value) {
            setState(() {          // ❌ UNNECESSARY!
              _password = value;   // This variable is also NOT in UI
            });
          },
        ),
        ElevatedButton(
          onPressed: () {
            // Send data to server
            _sendToServer(_username, _password);
          },
          child: Text('Submit'),
        ),
      ],
    );
  }

  void _sendToServer(String user, String pass) {
    // Send data...
  }
}

What’s wrong here? Every character typed triggers a complete widget rebuild, even though nothing visually changes! The user sees just the cursor blinking, but under the hood Flutter rebuilds the entire widget tree.
The right approach: If data is only needed for logic or server submission — store it without UI binding:

class _MyFormState extends State<MyForm> {
  // These variables are NOT used in build()
  final TextEditingController _usernameController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  @override
  void initState() {
    super.initState();
    // You can add listeners for additional logic
    _usernameController.addListener(() {
      print('Username changed: ${_usernameController.text}');
      // You can do validation, debounce, etc. here
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextField(controller: _usernameController),
        TextField(controller: _passwordController),
        ElevatedButton(
          onPressed: () {
            // Take data directly from controllers
            _sendToServer(
              _usernameController.text,
              _passwordController.text,
            );
          },
          child: Text('Submit'),
        ),
      ],
    );
  }
}

Key principle: If a variable isn’t displayed in build() and doesn’t affect how other widgets are displayed — it’s not part of UI state and doesn’t need setState().

Chapter 2: When setState IS Needed — Do It Precisely

There are cases where a variable truly affects UI, but that doesn’t mean you need to rebuild everything. Let’s look at smarter approaches.

❌ Typical case of excessive setState:

class ProductCard extends StatefulWidget {
  @override
  _ProductCardState createState() => _ProductCardState();
}

class _ProductCardState extends State<ProductCard> {
  bool _isFavorite = false;
  bool _isInCart = false;

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Column(
        children: [
          Image.network('...'),
          Text('Product Name'),
          Row(
            children: [
              IconButton(
                icon: Icon(
                  _isFavorite ? Icons.favorite : Icons.favorite_border,
                  color: _isFavorite ? Colors.red : Colors.grey,
                ),
                onPressed: () {
                  setState(() {          // ❌ Rebuilds the ENTIRE Card!
                    _isFavorite = !_isFavorite;
                  });
                },
              ),
              IconButton(
                icon: Icon(
                  _isInCart ? Icons.shopping_cart : Icons.add_shopping_cart,
                  color: _isInCart ? Colors.green : Colors.grey,
                ),
                onPressed: () {
                  setState(() {          // ❌ Rebuilds the ENTIRE Card again!
                    _isInCart = !_isInCart;
                  });
                },
              ),
            ],
          ),
        ],
      ),
    );
  }
}

Problem: We’re changing only the button color and icon, but rebuilding the entire product card with image, text, and all other elements!

✅ Solution 1: ValueNotifier for precise updates

class ProductCard extends StatelessWidget {
  final ValueNotifier<bool> _isFavorite = ValueNotifier<bool>(false);
  final ValueNotifier<bool> _isInCart = ValueNotifier<bool>(false);

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Column(
        children: [
          Image.network('...'),  // ❌ This part WON'T rebuild
          Text('Product Name'),   // ❌ This either
          Row(
            children: [
              // ✅ Only this icon will rebuild
              ValueListenableBuilder<bool>(
                valueListenable: _isFavorite,
                builder: (context, isFavorite, child) {
                  return IconButton(
                    icon: Icon(
                      isFavorite ? Icons.favorite : Icons.favorite_border,
                      color: isFavorite ? Colors.red : Colors.grey,
                    ),
                    onPressed: () {
                      _isFavorite.value = !isFavorite; // Just change value
                    },
                  );
                },
              ),
              // ✅ And this icon too
              ValueListenableBuilder<bool>(
                valueListenable: _isInCart,
                builder: (context, isInCart, child) {
                  return IconButton(
                    icon: Icon(
                      isInCart ? Icons.shopping_cart : Icons.add_shopping_cart,
                      color: isInCart ? Colors.green : Colors.grey,
                    ),
                    onPressed: () {
                      _isInCart.value = !isInCart;
                    },
                  );
                },
              ),
            ],
          ),
        ],
      ),
    );
  }
}

What changed:

  • Product image and text no longer rebuild on click
  • Only the specific icon updates
  • Code became more declarative

✅ Solution 2: Extract changing part into separate StatefulWidget

class FavoriteButton extends StatefulWidget {
  @override
  _FavoriteButtonState createState() => _FavoriteButtonState();
}

class _FavoriteButtonState extends State<FavoriteButton> {
  bool _isFavorite = false;

  @override
  Widget build(BuildContext context) {
    return IconButton(
      icon: Icon(
        _isFavorite ? Icons.favorite : Icons.favorite_border,
        color: _isFavorite ? Colors.red : Colors.grey,
      ),
      onPressed: () {
        setState(() {
          _isFavorite = !_isFavorite; // Only button rebuilds
        });
      },
    );
  }
}

// Use in main widget
class ProductCard extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Card(
      child: Column(
        children: [
          Image.network('...'),
          Text('Product Name'),
          Row(
            children: [
              FavoriteButton(),      // Isolated state
              CartButton(),          // Another isolated state
            ],
          ),
        ],
      ),
    );
  }
}

Chapter 3: Checklist and Practical Rules

The 5-Second Rule
Before writing setState(), ask yourself:

“If I change this variable, will the user see visual changes within the next 5 seconds?”

If no (data goes to server, saves to DB, used for calculations) — you don’t need setState().

Simple Test for Your Code
Try temporarily commenting out setState():

onChanged: (value) {
  // setState(() {          // Commented out
    _someValue = value;    // Data still saves!
  // });
}

If UI works correctly — setState was unnecessary.

Strategy Guide:

  1. Variable doesn’t affect UI → simple assignment
_apiToken = 'new_token';  // For sending requests
_userData = json;         // For processing
_cache = data;            // For storage

2 Affects one small element → ValueNotifier + ValueListenableBuilder
3 Affects several related widgets → extract to separate StatefulWidget
4 Affects entire screen/widget → setState() (use rarely and deliberately)

Typical Scenarios Without setState:

// Logging and analytics
onPressed: () {
  _clickCounter++;        // Just a variable
  _logEvent('button_click');
}

// Timers for logic
Timer.periodic(Duration(seconds: 30), (timer) {
  _lastUpdate = DateTime.now();  // For internal logic
  _checkForUpdates();
});

// Data caching
Future<void> _loadData() async {
  final data = await api.fetchData();
  _cachedData = data;     // Save, but don't rebuild
  _processData(data);
}

Conclusion: Ultimate Cheatsheet

When NOT to use setState:
• Data sent to server
• Saved to database/cache
• Used for logic/calculations
• For analytics/logging
• Temporary processing variables
When setState needed, but precisely:
• Button color/icon changes → ValueNotifier
• Text field updates → separate widget
• UI element state toggles → isolated state
When setState is justified:
• Large portion of screen changes
• Complex animation updates
• Entire list/table rebuilds

Remember: Flutter is not React. You don’t need to update state on every change. Separate UI state from business logic, and your app will run smoothly even on low-end devices.

Every unnecessary setState() is a step from “works fine” to “slideshow mode.” Be intentional about state management!
Buy Me a Coffee at ko-fi.com

How I Built a Hallucination Detector for RAG Pipelines in Python

How I Built a Hallucination Detector for RAG Pipelines in Python

Every developer who has shipped a RAG application knows this moment.

You retrieve the right documents. You pass them to the LLM. The response comes back confident, well-structured, and fluent. You ship it.

Then a user reports that the LLM cited a statistic that wasn’t in any of your documents. Or named a person who doesn’t exist. Or described a process that contradicts your source material — while sounding completely authoritative.

This is hallucination in a RAG pipeline. And it is surprisingly hard to catch systematically.

I built HallucinationBench to solve this in the simplest way possible.

The core idea

The approach is straightforward: use GPT-4o-mini as a structured judge.

Given a context (your retrieved documents) and a response (the LLM’s output), the judge:

  1. Breaks the response into individual factual claims
  2. Classifies each claim as grounded (supported by context) or hallucinated (absent or contradicted)
  3. Returns a faithfulness score and a verdict

That’s it. No embeddings, no vector databases, no infrastructure. Just one API call.

Usage

from hallucinationbench import score

context = """
The Eiffel Tower is located in Paris, France. It was constructed between
1887 and 1889 as the entrance arch for the 1889 World's Fair.
The tower is 330 metres tall.
"""

response = """
The Eiffel Tower is in Paris. It was built in 1889 and stands 330 metres
tall. It was designed by Leonardo da Vinci and attracts over 7 million
visitors every year.
"""

result = score(context=context, response=response)
print(result)

Output:

Verdict          : FAIL
Faithfulness     : 0.40

Grounded claims  (2):
  ✓  The Eiffel Tower is in Paris.
  ✓  It stands 330 metres tall.

Hallucinated claims  (3):
  ✗  It was built in 1889.
  ✗  It was designed by Leonardo da Vinci.
  ✗  It attracts over 7 million visitors every year.

Two genuine hallucinations caught cleanly — Leonardo da Vinci (it was Gustave Eiffel) and the 7 million visitor figure (not in the context).

The result object

result.faithfulness_score     # float 0.0 – 1.0
result.grounded_claims        # list of supported statements
result.hallucinated_claims    # list of fabricated statements
result.verdict                # "PASS" | "WARN" | "FAIL"
result.model                  # judge model used
Verdict Faithfulness Score
✅ PASS >= 0.8
⚠️ WARN >= 0.5
❌ FAIL < 0.5

How the judge prompt works

The system prompt instructs GPT-4o-mini to return raw JSON only — no markdown, no explanation, no code fences. Just the structured object.

{
  "grounded_claims": ["claim 1", "claim 2"],
  "hallucinated_claims": ["claim A", "claim B"],
  "faithfulness_score": 0.75
}

Two design decisions that matter here:

response_format: json_object — This is OpenAI’s native JSON mode. It guarantees the output is valid JSON on every call, eliminating parsing failures almost entirely.

temperature: 0 — Hallucination detection should be deterministic. The same context and response should always produce the same verdict. Temperature 0 enforces this.

Why GPT-4o-mini as the judge?

Three reasons:

Cost. Each evaluation costs approximately $0.001 — well under a tenth of a cent. You can run thousands of evaluations for a few dollars.

Speed. GPT-4o-mini is fast. A typical evaluation completes in under 2 seconds.

Accuracy. For claim-level classification against a provided context, GPT-4o-mini performs well. It is not doing open-domain knowledge retrieval — it is comparing claims against text you have already provided. That is a much simpler task.

Installation

pip install openai python-dotenv

Set your OpenAI API key:

# .env
OPENAI_API_KEY=your_key_here

Clone and run the Streamlit demo:

git clone https://github.com/bdeva1975/hallucinationbench.git
cd hallucinationbench
pip install -r requirements.txt
streamlit run app.py

What’s next

The roadmap for v0.2.0:

  • Batch evaluation — score multiple context/response pairs in one call
  • CSV upload in the Streamlit app
  • Custom judge model — bring your own OpenAI model
  • LangChain and LlamaIndex integration hooks
  • CI/CD example — run hallucination checks as a GitHub Actions gate before deployment

The bigger picture

As RAG applications move from prototype to production, hallucination detection is becoming a non-negotiable quality gate — not an optional debugging tool.

Gartner predicts that by 2028, 60% of software engineering teams will use AI evaluation and observability platforms to build user trust in AI applications, up from just 18% in 2025.

HallucinationBench is the lightweight, zero-infrastructure entry point to that category. No account, no cloud, no dashboard required. Just pip install and two lines of code.

GitHub: https://github.com/bdeva1975/hallucinationbench

Star it if you find it useful. PRs and feedback welcome.

Built with Python, OpenAI GPT-4o-mini, and Streamlit.

I Tried 30+ System Design Books and Courses: Here Are My Top 5 Recommendations for 2026

I tried 30+ platforms for system design: here are my top 5 recommendations

Disclosure: This post includes affiliate links; I may receive compensation if you purchase products or services from the different links provided in this article.

I tried 30+ platforms for system design: here are my top 5 recommendations

Hello Devs, in 2026, the AI era, System Design has become one of the most sought-after skills for software engineers, solution architects, and senior developers.

Whether you’re aiming for a FAANG-level interview, building the next unicorn startup, or designing scalable systems in AI, blockchain, or cloud computing, a deep understanding of system design is no longer optional — it’s essential.

Over the past few years, I’ve explored more than 30 books and courses from platforms like ByteByteGo, Educative.io, Udemy, Exponent, Codemia.io, Bugfree.ai, and Designgurus.io, plus a handful of top-rated books.

My goal was to find resources that don’t just explain concepts in theory but also show you how to apply them to real-world systems, with case studies, architectural diagrams, and hands-on practice.

After years of learning and testing, I’ve narrowed it down to my top 5 picks for mastering system design in 2026.

How I Chose These Courses and Platforms?

To ensure this list stays relevant and valuable, I evaluated each course based on these criteria:

  • Content Quality & Depth — Does it cover core topics like scalability, distributed systems, load balancing, caching, databases, and microservices?
  • Real-World Applications — Are there examples from actual systems like Netflix, Uber, WhatsApp, and ChatGPT?
  • Practical Learning — Does it include case studies, exercises, and diagram walkthroughs? Does the platform provide features for practicing?
  • Instructor Expertise — Are the instructors experienced engineers or architects with real-world experience?
  • Community & Support — Is there a way to ask questions, get feedback, or connect with other learners?
  • Industry Relevance — Are the concepts updated for current architectures like serverless, containerization, and AI-powered systems?

While not every course passes in every criterion, for example ByteByteGo and Designgurus.io were good on theory and general System Design concepts learning, but they don’t provide an environment to practice System Design questions.

I recommend you combine that with Codemia.io or Bugfree.ai to get the practice and AI mock interview part for complete practice.

Similarly, Exponent was great for mock interviews, but Educative.io and Bugfree.io’s AI mock interviews are also worth trying.

I Tried 30+ System Design Courses: Here Are My Top 5 Recommendations

My Top 5 System Design Interview Prep Courses and Platforms for 2026

Without any further ado, here are my top 5 recommendations for learning System Design for technical interviews. These courses and resources are not just to learn general System Design concepts, but also for solving company-specific questions and practicing online.

1. ByteByteGo — System Design Mastery (Premium Pick)

ByteByteGo is my number one recommendation if you want visual, practical, and up-to-date system design learning.

Founded by Alex Xu (author of System Design Interview — An Insider’s Guide), it’s a goldmine of beautifully crafted diagrams, step-by-step explanations, and case studies covering everything from classic designs to modern AI-driven architectures.

Key Features:

  • Deep dives into 50+ real-world system architectures
  • Updated for 2026 (serverless, edge computing, LLM integrations)
  • Animated visuals and flow diagrams
  • Weekly new content drops

Perfect For: Engineers who want a premium, all-in-one system design resource that blends theory, visuals, and practice.

They have also done a full website refresh recently as part of their relaunch and added all the content of their 7 best-selling books on System Design, OOP Design, ML System Design, and Generative AI Design on the platform.

They are now also offering 50% discount on their lifetime plan, the one I bought and the one I highly recommend to my students and readers.

Here is the link to join — Get 50% discount

Is ByteByteGo good place to learn System Design

They also provide ByteByteGo 101, a collection of 101 coding questions divided into key coding interview patterns like two pointers, fast and slow pointers, etc., for practice, which makes them the complete package for tech interviews in 2026.

2. Grokking the System Design Interview — DesignGurus.io

The original Grokking course is a favorite for FAANG interview prep. It breaks down system design problems into a clear, repeatable framework so you can approach any interview question with confidence.

Key Features:

  • Covers 15+ popular interview problems
  • Step-by-step solution walkthroughs
  • Includes scalability, fault tolerance, and data partitioning concepts
  • Fully interactive coding environment

Perfect For: Engineers focused on nailing system design interviews at top tech companies.

Here is the link to join this course — Grokking the System Design Interview

Is DesignGurus.io good place to learn System Dseign

And, if you like these Grokking interview courses from DesignGurus.io, you can get all of them for a big discount by purchasing their All course bundle.

This bundle includes their most popular Grokking courses like Grokking System Design Interview, OOP Design, Coding Patterns, as well as Grokking Dynamic Programming Interview. You can also use code GURU to get 55% discount now.

3. System Design Masterclass (2026) — Udemy

This Udemy bestseller is taught by FAANG engineers and focuses on practical interview readiness. You’ll work through popular real-world problems while mastering the fundamentals of architecture design.

Key Features:

  • 20+ case studies and design walkthroughs
  • Covers scalability, APIs, databases, and distributed systems
  • Affordable, often available at 80% off during sales
  • Lifetime access to all updates

Perfect For: Budget-conscious learners who want high-quality video-based learning from experienced engineers.

Here is the link to join this course — System Design Masterclass (2026)

best udemy course to leanr System Design

By the way, Udemy also has many great System Design courses which are worth checking out, like the one from Frank Kane, ex Amazon Hiring Manager, and Sandeep’s System Design course.

And, if you want to join multiple courses on Udemy, it may be worth getting a Udemy Personal Plan, which will give instant access to more than 11,000 top-quality Udemy courses for just $30 a month.

If you have a lot of time and want to save money, Udemy Personal Plan will be perfect for you.

4. System Design Interview Prep — Exponent

Exponent is known for interactive, mock interview-style preparation. Their system design course simulates real interview scenarios, complete with frameworks, whiteboard practice, and peer feedback.

Key Features:

  • Live and asynchronous practice sessions
  • Feedback from experienced interview coaches
  • Video guides and question banks
  • Tailored for tech interview success

Perfect For: Engineers who want hands-on interview simulation before stepping into the real thing.

Here is the link to join this course — System Design Interview Prep — Exponent

Is Exponent a good place for system desing interview prep

Another great thing I like about Exponent is their Mock interviews. They provide 5 free mock interviews with peers, which also replenish when you get good feedback. These are great to practice in an interview setting and also learning from peers.

And, if you have any FAANG interviews lined up, then you can also use their FAANG experts to interview for a real test. This is not free, but within $200, you can get a real shot at what to expect at FAANG interviews. I highly recommend them to you if you have an interview with Google, Amazon, or Meta.

I also suggest you get an Exponent subscription to watch this course, which you can get for just $12 a month now (U.P. $79 per month), which is like a 70% discount.

The membership will not only give you access to this course but also to all of their courses, mocks, videos, private community, and AI features for better interview preparation.

5. Grokking the Modern System Design Interview — Educative.io

This one is another great System Design interview course you can join to prepare for FAANG interviews from one of the most interactive learning platforms, Educative.io.

Once you’ve mastered the basics, this course dives deep into complex, large-scale systems — think designing YouTube-scale platforms, AI pipelines, and multi-region deployments.

Key Features:

  • Advanced topics like event-driven architecture, CQRS, and Kafka
  • Real-world examples from high-scale systems
  • Clear diagrams and problem breakdowns

Perfect For: Mid-to-senior engineers looking to level up their architectural thinking.

Here is the link to join this course — Grokking the Modern System Design Interview

Is Grokking the System Design good course for system design interview

Btw, you would need an Educative subscription to join this course, which not only provides access to this course but also more than 1200+ courses to learn essential tech skills, prepare for coding interviews, and improve yourself as a Developer.

If you want to invest in yourself and upgrade your tech skills, this is one subscription you definitely need. They are also offering 60% discount now.

Bonus Recommendations (Bugfree.ai and Books)

Apart from these, Bugfree.ai is another site that caught my attention, and I spent quite a lot of time there.

If you prefer learning by doing rather than just watching videos, Bugfree.ai is a powerful platform to practice System Design in a more hands-on way.

Instead of only consuming theory, you get to actually design systems and receive structured feedback, which makes a big difference when preparing for real interviews.

What makes Bugfree.ai stand out is its AI-assisted evaluation that reviews your design approach, highlights gaps, and suggests improvements — almost like having a mock interviewer available anytime. This is especially useful when you don’t have a study partner or mentor to review your solutions.

Key Features:

  • AI feedback on your system design answers
  • Real interview-style system design questions
  • Focus on structuring answers clearly and covering trade-offs

Perfect For: Engineers who already know the basics and want to sharpen their interview communication and system design thinking through practice.

Here is the link to join —Bugfree.ai (25% OFF)

Is Bugfree.ai is a good place to learn system design

And, if you want to complement these courses with books, I recommend:

These books are timeless references that deepen your understanding of distributed systems and large-scale architecture.

You don’t need too many books; just read these two books in detail, and you will be really well-shapen to answer System design questions in interviews.

Why ByteByteGo Stands Out for System Design Interview Prep?

While all these courses are excellent, ByteByteGo earns my premium pick for three reasons:

  1. Visual-First Learning — They have the best visual diagrams on all the topics on System Design, which make even the most complex systems easy to understand.
  2. Constant Updates — The platform keeps pace with modern architectures, from microservices to AI integrations. They have now uploaded all the content from their 7 best-selling books on Object Oriented Design, Machine Learning System Design, Gen AI System Design, and even coding patterns and behavioral interviews.
  3. Practical Depth — You’re not just learning theory — you’re exploring real-world production systems. Most of their content is written by FAANG engineers who have experience on both sides of the table.

If budget allows, I suggest starting with ByteByteGo for mastery and pairing it with the likes of Codemia.io or Bugfree.ai to get the practice and AI mock interview for best preparation of FAANG interviews in 2026.

They are now also offering 50% discount on their lifetime plan, which means they are now even more valuable, and once you buy their lifetime plan, which is just the cost of a couple of years, you won’t need any resources.

Every time you need to prepare for an interview or just want to stay up to date, you can use the platform.

Here is the link to join — Get 50% discount

why ByteByteGo is the best place for system design interview prep

They also provide ByteByteGo 101, a collection of 101 coding questions divided into key coding interview patterns like two pointers, fast and slow pointers, etc. I am practicing there now almost every day.

Final Thoughts

That’s my list of the top 5 system design courses for 2026. Whether you’re preparing for a big tech interview, designing complex systems at work, or just looking to level up your architecture skills, these resources will give you the frameworks, knowledge, and confidence to succeed.

With consistent study and practice, you’ll be well-prepared to tackle any system design challenge that comes your way in 2026.

All the best with your System Design Interviews. If you have any doubts or questions, feel free to ask in the comments.

P. S. — If you want to do just one thing, then join ByteByteGoand learn System Design Concepts and practice coding interviews, you will thank me later. It’s one of the most comprehensive resources for not just coding interviews but also for senior engineers to get better at their work. They are also offering 50% Discount now.

Understanding DDD Aggregates Through Board Games and Battlefields

I’ve been learning DDD for almost two years now. My understanding of Aggregate kept evolving — from accidentally treating it as just a Write Model, to roughly grasping that “an Aggregate protects invariants.” But something always felt unclear. Then a couple days ago, while explaining it to a teammate, I tried using a game analogy, and suddenly everything clicked.

An Aggregate Is a Board Game

Think of a board game. One game is one Aggregate. It has characters, items, and events — but most importantly, it has rules. Every action taken inside that game must follow those rules. The rules of Catan only apply in Catan. You can’t bring Monopoly’s “collect $200 when passing Go” rule into it.

Catan itself is the Aggregate — not the characters, not the items, but the game as a whole. Once you have those characters and items living inside the Aggregate, you can design Commands to operate on them. So an Aggregate protecting its invariants is really just like a board game enforcing its rules. The only difference is that in the real world, those rules are called business rules. And in the real world, there’s no escaping them.

Within a single turn, every action must be resolved at once — you can’t do half a move and come back later. This maps to another core concept:

An Aggregate is a Transactional Consistency Boundary. Either everything succeeds, or everything fails. No in-between.

The Battlefield vs. The War Room

Later, while discussing with another teammate, I needed to explain how the same entity can exist across two different Aggregates. I found that a war room sand table analogy works surprisingly well.

The real battlefield is one Aggregate. The commander’s sand table is another. On the sand table, flags represent each general — but the flag is not the general. It’s just an identifier (an ID) that tells the commander “this person exists.” The general’s full state lives on the battlefield.

The two Aggregates have completely different concerns about the same person. The sand table doesn’t need to know how badly the general is wounded or how many troops he has left. It only needs to know: “is he still available?”

That’s why across Aggregate boundaries, you only store an ID — never an object reference. Boundaries must stay clear.

When something happens on the battlefield, a messenger rides to the war room and the commanders update the sand table. That messenger is what DDD calls a Policy. It listens to a Domain Event fired by one Aggregate, then decides whether to trigger a reaction in another.

This is exactly why DDD pairs so naturally with EventSourcing and CQRS — the entire system collaborates through events flowing between boundaries. Not direct calls. Not shared objects. Events.

Wrapping Up

Once these two things clicked, a lot of design decisions that used to feel stuck suddenly had clear answers.

I’m not sure these analogies will work for everyone, but for me, they were the first time DDD stopped feeling like a pile of jargon. Sometimes you’re not stuck because you’re not trying hard enough — you just haven’t found the right angle yet.

If you’ve had a similar moment of clarity, I’d love to hear about it in the comments.

My Plan Before Coding

Many developers dive straight into git init, but the most critical work happens before the first line of code is written. Today, I’m walking you through my 7-step planning process using a Digital Marketplace backend as our case study.

1️⃣ The Strategy (The “Ask”)
Before writing code, I define the “Why.” Skipping this leads to feature creep and wasted hours.

Three Critical Questions:

Who is this for? (Target Audience)

What exact problem am I solving? (Pain Points)

What does success look like? (Definition of Done)

Example Case: Digital Marketplace

Users: Creators selling ebooks, courses, and digital assets.

Problem: Manual payment verification and insecure file delivery.

Success: A creator uploads a file, a user pays via local gateways (like Paystack), the product is delivered instantly, and earnings reflect in a dashboard.

2️⃣ Defining the MVP
What are the “must-have” features to get from zero to one? Focus on the core loop:

[ ] Identity: Authentication with User & Creator roles.

[ ] Management: File upload and storage for creators.

[ ] Checkout: Integration with local payment gateways.

[ ] Verification: Robust webhook handling for payment status.

[ ] Delivery: Automated email/secure download links.

[ ] Finances: A wallet system to track creator balances.

3️⃣ System Design (High-Level Thinking)
Visualizing data flow prevents bottlenecks later. Here is the logic for a purchase:

User → API → Backend → Database
↓
Payment Gateway
↓
Webhook → Backend → Update DB → Deliver Product

4️⃣ Database Design (The “Critical” Step)
This is where most projects fail. Designing a solid schema early saves days of refactoring.

Click to view the SQL Schema Entities

User: id, email, password, role (creator/customer)

Product: id, creator_id, title, price, file_url

Order: id, user_id, product_id, status (pending, paid, failed)

Transaction: id, order_id, amount, reference, status

Wallet: creator_id, balance

5️⃣ Offloading Background Tasks
Not every process belongs in the main request-response cycle. To keep the API snappy, I use Celery + Redis for:

Emails: Sending purchase confirmations.

Retries: Handling failed payment verification pings.

Security: Generating expiring, one-time-use download links.

6️⃣ Security Thinking (Day Zero)
Security isn’t a “later” feature. It’s a foundation:

Validation: Strict input sanitization to prevent injection.

Auth: Protecting endpoints with JWT or session-based security.

Storage: Using Signed URLs to ensure only paid users can access files.

Rate Limiting: Protecting APIs from brute force or DDoS attempts.

7️⃣ Infrastructure & Deployment
Decide on your stack before you start to avoid “environment hell.”

Containerization: Docker for local development and production consistency.

Hosting: Scalable providers like AWS or DigitalOcean.

Storage: S3 or Cloudinary for reliable asset hosting.

What do you think?
Do you have a different planning process, or do you prefer to “build and break” as you go? Let’s discuss in the comments!

I Tried to Run Symphony for Real on Rentello. It Broke in Exactly the Right Place.

Linear issue created

A few weeks ago, I published a build diary about porting OpenAI’s Symphony to Claude Code.

That piece was about getting the machine to exist.

This one is about what happened when I tried to trust it.

Not in a toy repo. Not with a fake ticket. Not with a carefully staged demo that only exercised the happy path. I wired the workflow into a real project, Rentello, pointed it at a real Linear board, and tried to make Codex, Claude, and Symphony-family orchestrators coexist on the same execution model.

That is when the most useful bug of the whole project appeared.

It wasn’t a crash inside the orchestration loop.
It wasn’t a retry bug.
It wasn’t even a Linear label bug.

It was a hidden architectural assumption inside one innocent file: WORKFLOW.md.

Phase two of the same story

The first article was the infrastructure story.

I had taken OpenAI’s Symphony architecture, ported the orchestration ideas to work with Claude Code, and documented the engineering journey: configuration, polling, workspaces, retries, MCP tooling, the terminal dashboard, the CLI quirks, the Linear GraphQL edges. It was the “can I build this?” phase.

This sequel starts after that.

Once the port existed, the obvious next question was: can this become a reliable workflow across agent surfaces?

I didn’t just want a Claude-only orchestrator anymore.

I wanted a system where:

  • planning could happen in Codex App or Codex web
  • work could be split into Linear issues that were safe for parallel or sequential execution
  • exec:agent work could be handled by generic agent surfaces
  • exec:symphony work could be handled by Symphony-family orchestrators
  • the same repository could remain deterministic whether the active surface was Codex or Claude

That sounds tidy when you say it fast.

In practice, it meant turning “AI can work on tickets” into an actual contract.

The experiment: make the workflow explicit

The first big change was conceptual, not technical.

I stopped treating the workflow as an informal set of conventions and turned it into a real repository contract:

  • shared execution-owner labels: exec:agent and exec:symphony
  • wave labels like wave:1, wave:2
  • a stable issue template
  • a single persistent ## Workpad comment per issue
  • a shared machine-readable contract plus shared docs/templates

The important detail here is that I did not want separate “Codex rules” and “Claude rules” at the planning layer.

The workflow itself needed to be generic.

The orchestrator choice should affect how the work is run, not how the work is defined.

So I introduced a shared contract and kept runtime-specific prompts only where they belonged: under .codex/ and .claude/, as adapters to the same underlying rules.

That gave me a clean execution model:

  • exec:agent means “owned by the non-Symphony agent surface”
  • exec:symphony means “owned by a Symphony-compatible orchestrator”
  • blockedBy remains authoritative
  • wave labels are scheduling metadata, not permission to ignore dependencies

That was the theory.

Then I decided to test it for real.

The smoke test: two issues, one dependency, no excuses

I created a new Linear project called Agent Workflow Smoke Test.

Then I used the repo’s planning contract to generate the smallest meaningful DAG I could think of:

  • MAS-26 — exec:agent, wave:1, research, docs
  • MAS-27 — exec:symphony, wave:2, research, docs, blockedBy MAS-26

The goal was deliberately low-risk: a docs-only smoke test with a real sequencing edge.

If the contract was sound, I should be able to verify:

  1. planning against the real repo
  2. issue creation in Linear with the right labels and dependency edges
  3. Codex App dispatch respecting exec:agent
  4. Symphony respecting exec:symphony
  5. a single persistent workpad model across the whole system

The first pass went well.

The repo-level checks passed.
The Linear issues were created correctly.
The labels were right.
The blockedBy relation was correct.

Then I moved MAS-26 into Todo and let Codex App handle the bootstrap.

That part worked exactly the way I wanted.

Codex App picked up only the exec:agent issue, moved it to In Progress, and created a single ## Workpad comment. More importantly, the next run reused that same comment and updated it in place instead of spamming the issue with milestone chatter.

That detail matters more than it sounds.

A lot of agent workflows die the death of a thousand comments. If every automation pass creates another “status update,” the issue becomes unreadable. Reusing one persistent workpad is the difference between an automation system that looks operationally credible and one that looks like a bot farm.

Linear issue created

Notes in the issue

At that point, the first conclusion felt obvious:

the workflow contract was working.

That conclusion was wrong.

The first false conclusion

Once the Codex App leg worked, I moved to the Symphony leg.

I launched openai/symphony against the repo’s WORKFLOW.md.

And immediately noticed something odd: it was looking at the real Rentello project instead of the smoke-test one.

The reason was simple in hindsight.

WORKFLOW.md was doing two very different jobs at once:

  1. it was the shared execution contract
  2. it was the runtime entrypoint for a specific orchestrator, with a specific project slug and a specific launcher configuration

That meant my “shared workflow file” wasn’t really shared at all.

It was secretly carrying environment-specific assumptions:

  • which Linear project to poll
  • which states count as active
  • which command to spawn for the agent runtime
  • what approval policy and sandbox mode to use

So before I even got to the deeper integration problem, I had already hit a design smell:

I was treating a runtime wrapper as if it were a universal contract.

Wrong project slug in symphony

I worked around it temporarily with a copied workflow file that targeted the smoke-test project.

That got me to the real failure.

The real failure: not labels, not blockers, not Linear

The next visible symptom was noisy and misleading.

Symphony would pick up the smoke-test issue, then back off with timeouts and failed runs.

At first glance, it looked like the kind of thing that sends you down the wrong rabbit hole:

  • maybe the new exec:* label routing was wrong
  • maybe the issue state transitions were inconsistent
  • maybe the blockedBy logic had a bug

None of that was the root cause.

The actual problem was much deeper and much simpler:

openai/symphony expected a Codex-compatible app-server runtime, while the repo’s WORKFLOW.md was still configured for Claude-style execution.

In other words, the orchestrator and the launcher contract disagreed about what “the agent” even was.

The workflow file still contained a Claude-oriented runtime block. openai/symphony expected something more like:

  • a Codex app-server command
  • a compatible approval policy
  • the sandbox settings the OpenAI implementation expects

So the visible failure was timeout and retry noise.
The real failure was a runtime mismatch.

That distinction matters.

If I had blamed the new workflow model, I would have “fixed” the wrong layer.

The issue was not exec:agent vs exec:symphony.
The issue was not the shared issue template.
The issue was not the workpad model.

The issue was that one literal WORKFLOW.md cannot honestly be the runtime entrypoint for both openai/symphony and a Claude-oriented Symphony port.

The fix that changed the architecture

This was the moment the architecture got better.

The correct lesson was not “make WORKFLOW.md more clever.”

It was: stop asking one file to represent two incompatible runtime contracts.

So I split the model into three layers:

  1. a shared execution contract
  2. a shared Symphony instruction body
  3. runtime-specific workflow wrappers

Concretely, the repo now has:

  • a shared machine-readable contract for labels, states, and workpad expectations
  • shared docs/templates for planning and issue execution
  • a shared Symphony body template
  • WORKFLOW.openai.md
  • WORKFLOW.claude.md
  • a small render script to generate the wrappers from the shared body

That was the missing separation all along.

The shared workflow body defines how the issue should be executed.

The runtime wrapper defines how the orchestrator should launch the agent.

Those are not the same concern.

Trying to force them into one file had worked only because I was not yet testing both runtimes seriously enough.

Architecture diagram

Once I made that split, the system became easier to reason about immediately.

The repository now says, in effect:

  • the execution rules are shared
  • the runtime launcher is not

That is a much healthier contract.

The second failure: the GraphQL ghost was still waiting

Of course, fixing the runtime mismatch didn’t magically make the full run clean.

After the split, Symphony could start correctly.
It could pick up MAS-27.
It could begin doing real work.

And then it hit another boundary failure: stale assumptions in the Linear GraphQL layer.

The logs told the story:

  • Field "identifier" is not defined by type "IssueFilter".
  • Cannot query field "blockedByIssues" on type "Issue".

This was a different class of bug entirely.

Now the runtime was correct, but the orchestrator’s GraphQL expectations were out of sync with the actual Linear schema. The result was a partial execution: the issue moved into In Progress, but the run did not complete cleanly, and no workpad comment was created on MAS-27.

That was important to verify because from the outside, a half-working orchestration system can look deceptively healthy.

The dashboard shows activity.
The issue state changes.
The agent session starts.

But the operational contract is still broken if the workpad never appears and the run dies on a schema edge.

This is a schema/client mismatch, not a workflow-contract bug.

What actually worked

This is the part I care about most, because I don’t want the story to sound more broken than it really was.

A lot did work:

  • the shared execution-owner model with exec:agent and exec:symphony
  • the planning contract based on shared docs/templates
  • the Linear issue generation for MAS-26 and MAS-27
  • blocker gating via blockedBy
  • Codex App automation dispatching only exec:agent
  • in-place reuse of a single ## Workpad comment on Linear
  • Symphony respecting the exec:symphony lane
  • the runtime split into WORKFLOW.openai.md and WORKFLOW.claude.md

That is not a small list.

In fact, the test did exactly what a good smoke test should do:

it validated the architectural core, then exposed the next real boundary failure.

First the runtime wrapper problem.
Then the stale GraphQL problem.

That is progress.

The actual lesson

If you want cross-agent orchestration, the wrong abstraction is “one workflow file for everything.”

What you actually need is:

  • one shared execution contract
  • one shared behavioral prompt body, if the execution model is the same
  • separate runtime launch wrappers for each orchestrator/runtime pair

The reason is simple.

Codex and Claude are not just different models. In this kind of system, they are different runtime surfaces with different launcher assumptions, different session protocols, and different orchestration expectations.

The workflow itself should stay stable across those surfaces.

The launcher should not.

That is the architectural correction this test forced me to make.

And in hindsight, it’s exactly the kind of correction you only get from trying to run the system for real.

The happy path rarely teaches you where your abstractions are lying.
Live orchestration does.

Same workflow rules, different launchers.

Where this goes next

The runtime split is in.
The generic exec:agent / exec:symphony workflow is in.
The Codex App leg is validated.

The next cleanup is clear:

  • patch the stale Linear GraphQL assumptions in the Symphony runtime
  • rerun the MAS-27 path cleanly
  • verify the full end-to-end chain with both executor lanes

That will be the point where the system stops being “an interesting orchestration prototype” and starts becoming something I would trust against a real backlog.

And honestly, that is the part I find most interesting now.

Porting the orchestrator was fun.

Finding the seam between shared execution contracts and runtime-specific launch contracts was the part that actually made it usable.

I got tired of juggling terminal windows for every AI agent, so I built an all-in-one IDE

https://dqy38fnwh4fqs.cloudfront.net/UHNN7ND8LGD6NEPIJJND6NGQ7LKO/blog/blog-content-image-e9d7dbb5-1e3f-49d5-a3cf-08caf79aa6f3.webp

If you use AI coding agents like Claude Code, Codex CLI, Gemini CLI, or Amp, you probably have a mess of terminal windows scattered across your screen.

Switching between projects means losing context, and there’s no easy way to see what all your agents are doing at once. I kept closing everything at the end of a session and spending the first 10 minutes of the next one just getting back to where I was.

So I built something to fix that.

What is 1DevTool?

1DevTool is an Electron app that gives you a single persistent workspace per project — multiple real terminals, a file explorer, code editor, HTTP client, database client, and an embedded browser, all in one window.

The core idea: your AI agents shouldn’t live in isolated terminal tabs. They should coexist in a layout you control, with full context preserved across sessions.

https://dqy38fnwh4fqs.cloudfront.net/UHNN7ND8LGD6NEPIJJND6NGQ7LKO/blog/blog-content-image-e9d7dbb5-1e3f-49d5-a3cf-08caf79aa6f3.webp

https://dqy38fnwh4fqs.cloudfront.net/UHNN7ND8LGD6NEPIJJND6NGQ7LKO/blog/blog-content-image-90a8e372-5d0d-4f32-a2a3-580d2b8306eb.webp

Key features

🔲 Multi-agent layout
Run multiple AI agents simultaneously in a 2×2 grid, columns, or single-focus layout. See everything at once.

💾 Terminal persistence
Sessions survive app restarts via tmux. Your scrollback is always there — no more re-running setup scripts every morning.

🔔 AI Activity Feed
Live notifications when any agent finishes a task or generates files, across all your active projects.

✂️ Send code to AI
Select code in the editor, right-click, and send it to any running agent — with file path and line numbers included automatically.

🔄 Resume AI sessions
Browse and continue past conversations from Claude Code, Codex, Gemini CLI, and Amp directly from the sidebar.

🧠 Skills Manager
Browse, install, and manage AI agent skills (CLAUDE.md, agent rules, etc.) with built-in security scanning.

🎨 VS Code themes
Import any .vsix theme file and it applies across the entire workspace — editor, terminal, panels.

🗄️ Database client
Connect to Postgres, MySQL, MongoDB, Redis, ClickHouse, and 8 more engines without leaving the window.

📂 Drag & drop projects
Drop a folder from Finder (or Explorer) onto the sidebar to add it instantly.

🧩 Customizable layouts
Drag panels between sidebars, save layout presets, and show/hide header items to match your workflow.

Availability & pricing

Available on macOS, Windows, and Linux.

Free tier: 1 project, 4 terminals — enough to try it with a real workflow.

🔗 1devtool.com

Happy to answer any questions or hear feedback in the comments. If you’ve run into the same terminal chaos with AI agents, I’d love to know how you’re currently handling it.

How to Deploy Next.js to Flux Cloud: Decentralized, High-Uptime, and Budget-Friendly

If you are a Next.js developer, you are probably used to the “Vercel way” of doing things. It is great, but as projects grow, the costs can scale quickly, and you are tied into a single provider’s ecosystem.

Flux Cloud recently released their new Deploy with Git feature. It is a decentralized alternative that offers a “push to deploy” experience but runs your app across a global network of independent nodes.

Here is why it is a solid choice for Next.js projects and how to get set up.

Why Next.js on Flux Cloud?

  • Drastically Lower Costs: Because Flux is a decentralized network, you aren’t paying the “convenience tax” of major corporate clouds. It is often a fraction of the price for the same CPU and RAM.
  • Automatic Global Redundancy: Your Next.js app isn’t just on one server. It is deployed across multiple nodes globally. If one node goes offline, the network keeps your site reachable.
  • Built-in Failover: If a deployment fails, Flux automatically rolls back to the last working version.
  • Full SSR Support: Unlike static-only hosts, Flux handles full-stack Next.js features, including Server-Side Rendering (SSR) and API routes, since it manages the runtime environment for you.

Step-by-Step Deployment

1. Prep your Repository

Flux makes it easy by detecting your framework. Ensure your package.json has the standard Next.js scripts:

  • "build": "next build"
  • "start": "next start"

2. Configure the Deployment

You can use the official Flux boilerplate to ensure your environment variables and ports are mapped correctly.

View the Next.js Deployment Guide here:
https://github.com/RunOnFlux/deploy-with-git/tree/master/deploy-nextjs

3. Connect to Flux Cloud

  1. Go to the Flux Cloud Dashboard.
  2. Select Deploy with Git.
  3. Enter your GitHub/GitLab repo URL.
  4. Flux will build your image and distribute it across the network.

4. Push to Update

Just like you are used to, any push to your main branch triggers a fresh build. Your global URL will update automatically once the new version is ready.

Useful Links

Let me know if you try this out!

Before You Rely on AI for Coding, Fix Your Docs First

AI tools are everywhere now ChatGPT, Claude, Gemini, Copilot, Cursor, and Zed.

They can write code, refactor files, and explain complex snippets in seconds.

But there’s a hidden problem we are all starting to notice:

If your project has weak or no documentation, AI will add more confusion, not more clarity.

This post is for every developer who uses AI daily and wants their codebase to stay understandable and maintainable.

What AI Is Actually Good At

AI is incredibly good at:

  • Writing boilerplate and repetitive code.
  • Translating code between languages or frameworks.
  • Cleaning up or refactoring isolated pieces of code.
  • Explaining a single function or file.

If you give it a small, tightly-scoped problem, it usually excels.

Where AI Fails (Without Docs)

AI struggles when:

  • Only a few people know “how things actually work” in the system.
  • There is no single source of truth for APIs or data models.
  • System behavior differs from what the function names suggest.
  • Business rules live exclusively in Slack threads, Zoom calls, or people’s memories.

In these cases, AI doesn’t know your real context. It guesses.

Sometimes the guesses look smart, but they are structurally wrong.

That’s how you end up with:

  • Hallucinated libraries or outdated syntax (e.g., using React 16 patterns in a Next.js 14 app).
  • Duplicate logic.
  • Wrong assumptions about edge cases.
  • Code that “works locally” but breaks real production flows.

Good documentation reduces this guessing.

The Basic Docs Every Project Should Have

You don’t need massive wikis. You just need a few simple, reliable documents.

1. The “Agent Rules” File (AGENTS.md or .cursorrules)

This is the modern developer’s secret weapon.
Instead of hoping the AI guesses your tech stack, write a simple markdown file in your root directory (like AGENTS.md) that tells the AI exactly how to behave.

  • What UI framework are you using? (e.g., “Use MUI v5, never v4”)
  • What state management? (e.g., “Only use Redux Toolkit”)
  • What are your logging standards? (e.g., “Use Pino, never console.log”)

When you feed this file to your AI Assistant, it instantly writes code that matches your exact project architecture.

2. A Short README Per Service or Module

For each main service or module, the README should explain in plain English:

  • What does this do?
  • How do I run it locally?
  • What are the main inputs and outputs?
  • Which other services or modules does it call?

This is useful for new devs joining the project, your future self, and AI tools when you paste it into the prompt as context.

3. Clear API or Data Contracts

If your project exposes APIs or uses shared data models, you need:

  • An OpenAPI / Swagger file, or a GraphQL schema.
  • Example requests and responses.

With this contract in place, AI can flawlessly generate clients, SDKs, and unit tests. Without it, AI only sees random route handlers and guesses the payload.

4. Simple Decision Notes (ADRs)

When you make an important architectural decision, write a small note:

  • What was the problem?
  • What did we choose and why?
  • What are the trade-offs?

This stops others from repeating old discussions and gives AI the context it needs when you ask it to help plan future changes.

How AI and Docs Work Together

Docs and AI are not enemies. They are a feedback loop.

Here are some easy workflows:

  • Use AI to format your docs: Paste a rough brain-dump and ask, “Turn this into a clean README section.”
  • Use docs to ground your AI: Paste the API spec and ask, “Create unit tests for these endpoints.”
  • Use AI to find doc gaps: Paste a git diff and ask, “Which parts of our README might be outdated now based on these code changes?”

Docs give AI real context. AI then gives you speed.

My Simple Take as a Developer

Here’s how I think about it now:

  • Code is cheap to generate. Understanding is expensive.
  • Clean, small docs save a massive amount of time: fewer Slack pings, fewer “quick calls,” fewer “what does this do?” moments.
  • If important knowledge lives only in your head, your team (and your future self) will pay the tax later.
  • You don’t need perfect English or long pages. A clear bulleted list is enough.

Final Thoughts

AI is powerful, but it’s not magic. It doesn’t automatically know your system, your business rules, or your past decisions.

Before you lean harder on AI, fix the basics: READMEs, API contracts, simple decision notes, and an AGENTS.md file.

Start very small:
Pick one feature you are working on today. Improve its README. Next time you use AI on that part of the codebase, give it this context.

You’ll immediately notice that the answers get more accurate, the code suggestions get more useful, and your project feels less like a black box.