DesignDeveloper ExperiencePython

Programmatic Voice: Why We're a Python Library, Not a Dashboard

November 20, 2025 9 min readGuava Engineering

No-code voice builders are very good at one thing: demos. You drag some nodes around, connect a few arrows, type some text into boxes, and in fifteen minutes you have a bot that can handle a simplified happy path. It looks great in a Loom recording.

Then you try to deploy it to production.

What Goes Wrong

The problems don't appear all at once. They accumulate. First, you realize you can't write a unit test for a flow you built with a mouse. You have to run the whole thing, make a real call, and listen. Your QA process becomes manual and error-prone.

Then you try to reuse logic across multiple flows — maybe your appointment scheduling bot and your reminder bot both need to verify patient identity. In a visual builder, you copy-paste nodes. Now you have two copies of the same logic that will drift apart over time.

Then a developer who didn't build the original flow needs to make a change. There's no diff. There's no history of why a particular edge was added. There's a picture of some boxes.

Then you try to integrate with your existing systems — your CRM, your calendar API, your authentication layer. The visual builder has a webhook node, but composing asynchronous system interactions through a GUI is painful and fragile.

Then you try to scale. The flow that worked for one bot needs to handle hundreds of concurrent calls with different configurations. A visual builder has no parametrization model.

None of these are edge cases. They're the normal requirements of any production software system. And visual builders fail all of them because they made a fundamental mistake: they chose to abstract *away* from code rather than *toward* better code.

What Programmatic Voice Looks Like

Here's a realistic example: a patient recall bot that needs to verify identity, check appointment history, and offer rebooking. In a flow builder, this would be a 20-node diagram. In Guava:

python
import guava
from my_emr import get_patient, get_appointment_history, book_appointment

class PatientRecallBot(guava.CallController): def __init__(self, patient_id: str): super().__init__() self.patient = get_patient(patient_id)

self.set_persona( organization_name="Riverside Medical Group", agent_name="Alex", ) self.set_task( objective=f"Recall {self.patient.first_name} for an overdue follow-up appointment", checklist=[ guava.Field( key="identity_verified", field_type="boolean", description=f"Confirm patient DOB matches {self.patient.dob}", ), guava.Say( f"I'm calling because you're due for a follow-up with Dr. " f"{self.patient.primary_physician}. Our records show your last " f"visit was {self.get_last_visit_summary()}." ), guava.Field( key="appointment_time", field_type="calendar_slot", description="Book a follow-up appointment", choice_generator=self.get_available_slots, ), "Confirm the appointment and let the patient know what to expect.", ], on_complete=self.confirm_and_close, )

def get_last_visit_summary(self): history = get_appointment_history(self.patient.id) return history[-1].summary if history else "over six months ago"

def get_available_slots(self): return fetch_physician_availability(self.patient.primary_physician_id)

def confirm_and_close(self, result): book_appointment(self.patient.id, result["appointment_time"]) self.end_call() ```

This is forty lines of Python. It's version-controlled. It's testable — you can mock get_patient, get_appointment_history, and book_appointment and write unit tests against every branch. It's composable — you can import PatientRecallBot from another module, subclass it, or pass it as a parameter.

The Side-by-Side

What a flow builder gives you: a diagram that lives in a SaaS vendor's database, that two developers can't work on simultaneously, that produces no meaningful git history, that you cannot test without placing a real call, and that you will rewrite from scratch when the vendor changes their pricing or shuts down.

What Guava gives you: a Python class that lives in your repository, that can be reviewed in a pull request, that can be tested with pytest, that can be composed with your existing Python codebase, and that you own completely.

The visual builder crowd will tell you that no-code lowers the barrier to entry. We think they're solving the wrong problem. The barrier to entry for voice AI is not Python syntax. It's understanding telephony, latency, state management, and error handling. A drag-and-drop interface doesn't help with any of those things — it just makes them harder to see.

Code is not the problem. Code is the solution. We built Guava as a library because that's the only abstraction that lets you build real software.

Try Guava