When I set out to bring the first real feature of my AI assistant platform online, a booking system, I wasn’t just writing code. I was building the kind of foundations that let everything else stand securely. The work gave me not only a functioning feature but also confidence that the architecture could support growth.
I approached this in the same way I would approach any complex delivery: break it down into clear steps, tackle the dependencies in order, and always keep an eye on future maintainability. The difference here is that I am building on my own, where in a large enterprise the same job would span multiple teams and review boards.
Version-controlled schema
I started by wiring Alembic migrations into the platform. This gave me a version-controlled record of every change to the database schema. If you think of the database as the foundation of a house, migrations are like keeping detailed blueprints of every change to the structure.
It wasn’t flawless at first. I hit the familiar problem of mismatched down_revision values, which meant my migrations didn’t form a proper chain. Once fixed, though, the system worked as intended: a reliable history of schema changes that could be replayed anywhere.
In a large-scale enterprise this same step would usually involve:
- A dedicated database migration team to review the scripts.
- Change advisory boards to sign off the rollouts.
- Multiple environments (development, test, staging, production) with rollback plans agreed in advance.
I could iterate within hours; in an enterprise, that cycle would more likely take weeks.
Defining the booking model
Next I needed to define what a booking actually looks like in code. I created a SQLAlchemy model with the fields you’d expect: customer name, email, start time, end time, status, and notes.
class Booking(Base):
__tablename__ = "bookings"
id = Column(UUID, primary_key=True, default=uuid.uuid4)
customer_name = Column(String(120), nullable=False)
customer_email = Column(String(254), nullable=False, index=True)
starts_at = Column(DateTime(timezone=True), nullable=False, index=True)
ends_at = Column(DateTime(timezone=True), nullable=False)
status = Column(Enum("pending", "confirmed", "cancelled", name="booking_status"), nullable=False, default="pending")
notes = Column(Text(), nullable=True)
For me this was a practical modelling exercise: what exactly are the minimum attributes that define a booking.
In a corporate environment, the conversation would be broader. Multiple business units would need to agree on fields, naming standards, and compliance. Data architecture boards would ensure alignment with master data rules. Customer data, such as emails, would be checked against GDPR handling standards.
Working solo, I could decide and implement quickly, but I also had to take personal responsibility for getting the design right.
CRUD endpoints in FastAPI
With the model in place, I built the FastAPI endpoints for create, read, update and delete. This is the point where the system became “real”: external clients could now interact with bookings through a standard REST API.
In my build the path was direct: schema, model, API, persistence. In an enterprise project, those endpoints would usually sit behind an API gateway, be subject to formal design reviews, and include layers such as authentication, rate limiting, and monitoring.
The contrast is speed versus rigour. I could write code and test it against a local Postgres in minutes. At scale the same thing would involve design documents, API specifications, and contracts with downstream consumers before implementation began.
Testing as risk reduction
The final piece was testing. I wrote unit tests for the model and integration tests that spun up a test database, applied migrations, and exercised the CRUD endpoints.
For me, tests were about trust. I wanted to be sure that if I change something later, I don’t break bookings without noticing.
In a larger organisation, testing would be broader. Dedicated QA teams would handle regression packs. Pipelines would run the tests across multiple environments with dashboards showing coverage and defect trends. My version is leaner, but the principle is the same: catch problems early and avoid surprises in production.
Lessons along the way
This work wasn’t without its problems.
- At one stage migrations failed in CI because of a broken revision chain. The fix was to repair the link between baseline and booking migrations so Alembic could apply them in sequence.
- Linting and formatting tools flagged issues I’d missed. Ruff and Black became an extra safety net that kept the codebase consistent.
- I also ran into errors creating test databases. Some of these came down to Postgres restrictions around creating databases inside transactions, which required changes to how I set up test fixtures.
In an enterprise, those kinds of problems are often absorbed by specialists. Here, I had to wear every hat: database admin, DevOps, QA, and developer. Each fix made the platform more robust and my understanding deeper.
Wrapping up
By the end of this build I had:
- A version-controlled schema.
- A concrete booking model.
- CRUD endpoints that work end to end.
- Automated tests confirming functionality.
The work was not glamorous, but it was essential. I now know the scaffolding can hold the skyscraper. Everything else — the FAQ bot, sentiment analysis, forecasting — will rest on this foundation.
What I find most interesting is the comparison with large-scale enterprise projects. The ingredients are the same: schema, models, APIs, tests. The difference is in scale and governance. I could move quickly, solve problems directly, and accept personal accountability. In an enterprise, the same process would involve committees, approvals, and teams. One approach optimises for delivery speed, the other for organisational safety.
The real challenge is striking the right balance: building fast enough to deliver value, but with enough rigour that the work can later slot into an enterprise-grade delivery without starting over.
What’s your experience? Do you find yourself trading speed for safety, or have you found a way to balance the two?




Leave a comment