Skip to main content

Featured

SOLID PRINCIPLES

SOLID PRINCIPLES

SOLID Design Principles for Senior Python Engineers CLEAN ARCHITECTURE SERIES SOLID Principles in Python: The Senior Playbook 15 min read Series: Architecture & Scale Day 14 / 100 Level: Staff Engineer To master the system, you must first master its boundaries. In production-scale systems, applying SOLID isn't about dogmatic academic rules. It is about managing cognitive load, isolating failure domains, and ensuring codebases evolve without cascading regression failures. ⏳ Context: When working in Python—a highly dynamic, multi-paradigm language—SOLID architecture looks different than it does in compiled, static languages like Java or C++. We leverage structural subtyping (protocols), runtime decoupling, and dynamic interfaces while maintaining strict type safety to write code that scales with team size and infrastructure demands. 1. Single-Responsibility P...

Python State Machines: FSMs, The State Pattern & Transitions (2026)

Skip to main content

Day 25: The State Machine — Eliminating Boolean Blindness

  • Series: Logic & Legacy
  • Day 25 / 30
  • Level: Senior Architecture

Context: We have secured the network and built fault tolerance into our exceptions. But there is a silent killer in every codebase that no try/except block can catch. It is the bug of "Invalid State." Today, we master the Finite State Machine (FSM) to cure it.

The Disease: Boolean Blindness

Infographic contrasting Boolean Blindness with Finite State Machines. Showing how multiple boolean flags create logical paradoxes while FSMs ensure orderly transitions.

Look at the database schema of a typical e-commerce application. You will inevitably find an Order table plagued by multiple flags:

Mathematically, 4 boolean columns allow for 2⁴ (16) different states. What happens when a code glitch sets is_shipped = True AND is_cancelled = True? The system collapses into a Logical Paradox. To "fix" it, developers write unreadable nested if statements. This is Boolean Blindness.

1. The Cure: Finite State Machines (FSM)

A Finite State Machine (FSM) enforces one absolute architectural law:

Instead of 5 boolean flags, you use a single state field. An order is either DRAFT, PAID, or SHIPPED. It is physically impossible to be both Shipped and Cancelled simultaneously in this model.

2. The Architecture: Nodes, Triggers, and Edges

A professional state machine consists of three structural pillars:

  • 1. States (Nodes): The distinct statuses (e.g., Active, Inactive).
  • 2. Events (Triggers): Actions attempting state changes (e.g., pay(), cancel()).
  • 3. Transitions (Edges): Strict rules determining if a Trigger is allowed to move an object from State A to State B.

3. Python Implementation: The State Design Pattern

Senior Architects utilize the State Design Pattern. By leveraging Polymorphism and Abstract Base Classes (ABCs), we delegate logic to specific state classes, satisfying the Open-Closed Principle.

The OOP State Pattern Architecture
from abc import ABC, abstractmethod

class OrderState(ABC):
    @abstractmethod
    def pay(self, order): pass

class DraftState(OrderState):
    def pay(self, order):
        print("Payment processing...")
        order.set_state(PaidState())

class Order:
    def __init__(self):
        self._state = DraftState()
    
    def pay(self):
        self._state.pay(self)

4. Production Standard: Declarative FSM Libraries

While the OOP pattern is excellent for learning, enterprise-scale workflows (20+ states) become verbose. In production, we use Declarative FSM Libraries like transitions or django-fsm.

Declarative Transitions (Transitions Library)
# pip install transitions
from transitions import Machine

states = ['draft', 'review', 'published']
transitions = [
    {'trigger': 'submit', 'source': 'draft', 'dest': 'review'},
]

machine = Machine(model=article, states=states, transitions=transitions)

🛠️ Day 25 Project: The Document Flow

Build an unbreakable Document workflow system from scratch.

  • Replicate the OOP State Pattern for a Document object with Draft, InReview, and Published states.
  • Implement approve() and reject() methods. Rejection must move the state backward.
🔥 PRO UPGRADE (The Guard Clause)

Your challenge: Modify DraftState so the submit() trigger accepts a word_count argument. Implement a Guard Clause: the transition to InReview is only allowed IF word_count > 500. Otherwise, raise an exception.

5. FAQ: State Architecture

FSM vs. DAG (Directed Acyclic Graph)?

A DAG (like in Airflow) has a distinct start and end with no loops. An FSM allows cycles (e.g., LoggedOutLoggedInLoggedOut).

How do I persist State Machines in a database?

Save the state as a VARCHAR column. The FSM library wraps your ORM model; when a transition is triggered, the library validates the rule in memory and then issues a single UPDATE SQL command to sync the string.

Can I handle side effects (like sending an email) during a transition?

Yes. Most FSM libraries support before and after callbacks. This allows you to decouple your "Action" (emailing the user) from your "State Change" (setting order to Paid), ensuring the action only triggers on successful transitions.

When is a State Machine overkill?

If your object only has two states (e.g., Active/Inactive) and will never evolve, a simple boolean is fine. Use an FSM the moment you hit three states or have transition rules (e.g., "Cannot go from A to C directly").

Comments