When Frontend Reactivity Meets Backend Python: The Story of My First 1,000-Download Library

After years of building software for others, I decided to scratch my own itch and create something for the wider developer community. Today, I'm humbled to share that reaktiv, my first serious open source library, recently crossed 100 stars on GitHub and reached over 1,000 pip installs per month. This milestone feels significant not just because of the numbers, but because of the journey it represents - one filled with surprising challenges in communication rather than code. The Birth of reaktiv After spending several years developing IoT systems and dashboards using Angular, I found myself repeatedly frustrated when switching to Python for backend work. The elegant reactivity model I'd come to rely on in frontend development was missing. For those unfamiliar with reactive programming, it's a declarative paradigm focused on data flows and propagation of changes. In Angular, this looks like: // Angular-style reactivity const count = signal(0); const doubled = computed(() => count() * 2); const message = computed(() => `The count is ${count()}`); // When count changes, doubled and message update automatically count.set(5); I searched for a Python equivalent that would handle complex state and dependencies as elegantly as Angular's signals, but couldn't find anything that quite hit the mark. So I built reaktiv: # reaktiv's Python implementation from reaktiv import signal, computed, effect count = signal(0) doubled = computed(lambda: count() * 2) message = computed(lambda: f"The count is {count()}") # When count changes, doubled and message update automatically count.set(5) The core idea was simple: create a lightweight library that builds computation graphs with automatic dependency tracking, efficient updates, and first-class async support. The Communication Gap Here's where things got interesting. After releasing reaktiv, I encountered an unexpected challenge: explaining what problem it solved. Frontend developers who had worked with Angular or React immediately got it. "Oh, this is like React hooks but for Python!" or "This is Angular signals ported to Python!" But Python backend developers often responded with, "Why wouldn't I just use a pub/sub system?" The disconnect was revealing. In frontend development, UI state management is a well-known pain point. But in backend Python, especially in CRUD-focused applications, developers weren't feeling the same pain. When I'd explain, "It automatically detects and tracks dependencies," many would respond, "But I don't have complex dependencies in my API endpoints." I realized I was solving a problem many developers didn't know they had - or genuinely didn't have in their specific context. Finding the Right Audience The breakthrough came when reaktiv got featured on Hacker News and r/python. The discussions helped me understand where the value proposition was resonating - and where it wasn't. Developers working on: Real-time dashboards Monitoring systems Complex state management IoT applications Streaming data pipelines These were the folks who immediately saw the value. One comment captured it perfectly: "This reminds me of how Excel formulas update when cell values change - but for Python code." That was the "aha" moment. I wasn't building "Angular signals for Python" - I was building "Excel-like computation graphs for Python applications." The Magic of Automatic Dependency Tracking The core innovation in reaktiv is how it automatically tracks dependencies without requiring explicit declarations. Let me explain how this works: Runtime Dependency Detection: When a computed signal or effect runs, reaktiv creates a tracking context. Any signal accessed within that context is automatically registered as a dependency. Fine-grained Dependency Graph: Instead of broad pub/sub topics, reaktiv builds a precise directed acyclic graph (DAG) of computations, tracking exactly which values depend on which others. Dynamic Dependencies: The dependencies can change based on conditional logic. If a computed value sometimes uses signal A and sometimes doesn't based on a condition, reaktiv handles that automatically. For example: # This is where the magic happens def calculate_price(): base_price = product_price() # Automatically tracked as dependency if apply_discount(): # Also tracked return base_price * (1 - discount_rate()) # discount_rate tracked conditionally else: return base_price # discount_rate not tracked in this case total_price = computed(calculate_price) When apply_discount() returns True, reaktiv tracks discount_rate as a dependency. When it returns False, changes to discount_rate won't trigger a recalculation because it's not in the dependency path. This automatic tracking eliminates huge amounts of boilerplate code that would otherwise be needed to manually wire up dependencies or event handlers. It's what makes reaktiv feel almost magical

Apr 28, 2025 - 11:16
 0
When Frontend Reactivity Meets Backend Python: The Story of My First 1,000-Download Library

After years of building software for others, I decided to scratch my own itch and create something for the wider developer community. Today, I'm humbled to share that reaktiv, my first serious open source library, recently crossed 100 stars on GitHub and reached over 1,000 pip installs per month.

This milestone feels significant not just because of the numbers, but because of the journey it represents - one filled with surprising challenges in communication rather than code.

The Birth of reaktiv

After spending several years developing IoT systems and dashboards using Angular, I found myself repeatedly frustrated when switching to Python for backend work. The elegant reactivity model I'd come to rely on in frontend development was missing.

For those unfamiliar with reactive programming, it's a declarative paradigm focused on data flows and propagation of changes. In Angular, this looks like:

// Angular-style reactivity
const count = signal(0);
const doubled = computed(() => count() * 2);
const message = computed(() => `The count is ${count()}`);

// When count changes, doubled and message update automatically
count.set(5); 

I searched for a Python equivalent that would handle complex state and dependencies as elegantly as Angular's signals, but couldn't find anything that quite hit the mark. So I built reaktiv:

# reaktiv's Python implementation
from reaktiv import signal, computed, effect

count = signal(0)
doubled = computed(lambda: count() * 2)
message = computed(lambda: f"The count is {count()}")

# When count changes, doubled and message update automatically
count.set(5)

The core idea was simple: create a lightweight library that builds computation graphs with automatic dependency tracking, efficient updates, and first-class async support.

The Communication Gap

Here's where things got interesting. After releasing reaktiv, I encountered an unexpected challenge: explaining what problem it solved.

Frontend developers who had worked with Angular or React immediately got it. "Oh, this is like React hooks but for Python!" or "This is Angular signals ported to Python!" But Python backend developers often responded with, "Why wouldn't I just use a pub/sub system?"

The disconnect was revealing. In frontend development, UI state management is a well-known pain point. But in backend Python, especially in CRUD-focused applications, developers weren't feeling the same pain.

When I'd explain, "It automatically detects and tracks dependencies," many would respond, "But I don't have complex dependencies in my API endpoints."

I realized I was solving a problem many developers didn't know they had - or genuinely didn't have in their specific context.

Finding the Right Audience

The breakthrough came when reaktiv got featured on Hacker News and r/python. The discussions helped me understand where the value proposition was resonating - and where it wasn't.

Developers working on:

  • Real-time dashboards
  • Monitoring systems
  • Complex state management
  • IoT applications
  • Streaming data pipelines

These were the folks who immediately saw the value. One comment captured it perfectly: "This reminds me of how Excel formulas update when cell values change - but for Python code."

That was the "aha" moment. I wasn't building "Angular signals for Python" - I was building "Excel-like computation graphs for Python applications."

The Magic of Automatic Dependency Tracking

The core innovation in reaktiv is how it automatically tracks dependencies without requiring explicit declarations. Let me explain how this works:

  1. Runtime Dependency Detection: When a computed signal or effect runs, reaktiv creates a tracking context. Any signal accessed within that context is automatically registered as a dependency.

  2. Fine-grained Dependency Graph: Instead of broad pub/sub topics, reaktiv builds a precise directed acyclic graph (DAG) of computations, tracking exactly which values depend on which others.

  3. Dynamic Dependencies: The dependencies can change based on conditional logic. If a computed value sometimes uses signal A and sometimes doesn't based on a condition, reaktiv handles that automatically.

For example:

# This is where the magic happens
def calculate_price():
    base_price = product_price()  # Automatically tracked as dependency

    if apply_discount():  # Also tracked
        return base_price * (1 - discount_rate())  # discount_rate tracked conditionally
    else:
        return base_price  # discount_rate not tracked in this case

total_price = computed(calculate_price)

When apply_discount() returns True, reaktiv tracks discount_rate as a dependency. When it returns False, changes to discount_rate won't trigger a recalculation because it's not in the dependency path.

This automatic tracking eliminates huge amounts of boilerplate code that would otherwise be needed to manually wire up dependencies or event handlers. It's what makes reaktiv feel almost magical in complex systems - dependencies just work without you having to think about them.

Effects: Where Reactivity Meets the Real World

While signals store values and computed values derive new values, effects are where reaktiv connects with the outside world. Effects are functions that execute when their dependencies change, letting you synchronize reactive state with external systems.

Like computed values, effects automatically track their dependencies. Unlike computed values, they:

  1. Don't return values - they perform actions
  2. Can contain side effects (API calls, DOM updates, logging, etc.)
  3. Run asynchronously by default

Here's how effects complete the reaktiv triad:

from reaktiv import signal, computed, effect

# Base values
temperature = signal(22.5)  # in Celsius
temp_unit = signal("C")

# Computed value with automatic dependency tracking
display_temp = computed(lambda: 
    temperature() if temp_unit() == "C" else 
    temperature() * 9/5 + 32
)

# Effect that runs whenever temperature or unit changes
def update_dashboard():
    current_temp = display_temp()
    current_unit = temp_unit()
    print(f"Dashboard updated: {current_temp}°{current_unit}")
    # In a real app, this would update UI, send to monitoring, etc.

# Creating the effect automatically runs it once
dashboard_updater = effect(update_dashboard)  # Prints: "Dashboard updated: 22.5°C"

# Change the temperature - effect runs automatically
temperature.set(25.0)  # Prints: "Dashboard updated: 25.0°C"

# Change the unit - effect runs automatically again
temp_unit.set("F")  # Prints: "Dashboard updated: 77.0°F"

Effects can also be async functions that integrate perfectly with Python's asyncio:

import asyncio
from reaktiv import signal, effect

async def demo_async_effects():
    alerts = signal([])

    async def process_alerts():
        current_alerts = alerts()
        if current_alerts:
            print(f"Processing {len(current_alerts)} alerts...")
            # In real code, this might call an API or update a database
            await asyncio.sleep(0.5)  # Simulate async work
            print("Alerts processed and cleared")

    # Register the async effect
    alert_processor = effect(process_alerts)

    # Add some alerts - effect will run automatically
    alerts.set(["System overload", "Disk space low"])
    await asyncio.sleep(1)  # Give effect time to complete

asyncio.run(demo_async_effects())

Effects serve as the bridge between your reactive state and the outside world, making them essential for building complete reactive systems.

Reframing the Problem: Computational Dependency Graphs

With these insights, I rewrote the README to focus less on "reactivity" (a term familiar to frontend developers but carrying different connotations in backend contexts) and more on "computational dependency graphs" - a concept I hoped would resonate better with Python developers.

I began explaining it as:

reaktiv builds efficient computation graphs that only recalculate values when dependencies change. The system automatically tracks which computations depend on which data, eliminating the need for manual subscription management.

This framing helped backend developers see the pain points reaktiv actually addresses:

  1. Manual dependency tracking scattered throughout codebases
  2. State synchronization bugs when updates are missed
  3. Inefficient recalculation of values that haven't changed
  4. Cognitive load of maintaining complex dependency chains

The Data Science Connection: An Unexpected Frontier

Recently, I've noticed something interesting - a small but growing interest from data scientists who've stumbled upon reaktiv. This wasn't a community I originally targeted, but their interest makes sense in retrospect.

Some data scientists have expressed interest in using reaktiv for automatically updating computational pipelines - similar to how cells in Excel automatically recalculate. A few mentioned potential use cases in Jupyter notebooks, though tools like Marimo have already tackled reactive notebooks directly.

I'm still exploring what reaktiv might offer the data science community. The computational dependency graph approach seems like it could be valuable for:

  • Managing complex transformation pipelines where intermediate results depend on changing parameters
  • Creating exploratory data analysis tools that efficiently update visualizations when data changes
  • Building interactive dashboards where multiple interconnected analyses need to stay in sync
  • Handling streaming data scenarios where updates need to propagate efficiently through a processing pipeline

However, I'm still learning about data science workflows and pain points. I suspect explaining reaktiv as a "computational dependency graph" rather than a "reactive library" might help data scientists connect it to familiar concepts in their domain.

This is an area I'm actively exploring, and I'd love to hear from data scientists about whether reaktiv could solve real problems in their work - or if I'm still missing the mark.

Lessons Learned

Reaching 100 GitHub stars and 1,000+ monthly installations has taught me several valuable lessons:

  1. Communicating value is a skill unto itself. Building reaktiv was only half the battle - finding the right words to explain its benefits proved to be equally challenging.

  2. Different communities have different pain points. What's obvious to frontend developers might be completely foreign to backend or data science developers.

  3. Finding the right metaphor matters. Switching from "Angular signals for Python" to "Excel-like computation graphs" made the concept click for many more developers.

  4. Listen to confusion. When developers were confused about the value proposition, that wasn't their failure to understand - it was my failure to communicate.

  5. Open source is a conversation. The library improved dramatically based on discussions with users who approached the problem from angles I hadn't considered.

What's Next

The unexpected interest from the data science community has opened new possibilities for reaktiv. I'm currently exploring better integration with pandas, numpy, and other data science tools to make the library even more useful in that context.

I'm also working on performance optimizations for large computation graphs and better debugging tools to visualize dependencies.

Building reaktiv has been a humbling reminder that creating useful software isn't just about solving technical problems - it's about understanding the problems people actually have, and communicating solutions in ways that resonate with their experiences.

If you're interested in exploring reaktiv, you can find it on GitHub or install it with pip install reaktiv. I'd love to hear how you're using it or could envision using it!