How We Embedded in an Antigravity Codebase in 24 Hours and Shipped in 48

Keyur Patel
March 25, 2026
18 min
Last Modified:
April 22, 2026
Building apps on AI-native frameworks like Antigravity can unlock speed, but it also introduces a new class of risk: systems that evolve quickly without the grounding of deep architectural oversight. In this case, a Series A startup had reached strong product–market traction and built a functional agentic workflow layer entirely on top of Antigravity. The system worked, the product was gaining adoption, and the team was moving fast. But beneath that velocity was a growing concern—no one on the team had fully mapped the architecture end-to-end, and critical decisions had been made without a senior engineer ever stepping in to evaluate long-term implications.
When a high-stakes enterprise integration surfaced, the pressure on the system exposed those hidden gaps. The integration required a secure, production-grade endpoint capable of handling structured payloads, validating inputs, transforming data, and coordinating downstream processing all under strict enterprise security constraints. What should have been a straightforward extension quickly became a stress test for the entire architecture.
Our role was to embed directly into the codebase, rapidly build a mental model of how the system worked, and identify what would break under real-world constraints. What followed was not just about shipping a feature under pressure, but about understanding how agentic systems behave when pushed beyond their original assumptions, and how to bring structure, reliability, and clarity back into a codebase that had scaled faster than its design.

Hour 0 to 4: Reading the Codebase Cold
The first four hours of any embed engagement are non-negotiable. Nothing gets written. No suggestions get made. No fixes get discussed. The entire period is reading.
This is not how most developers approach an unfamiliar codebase. The instinct, especially under time pressure, is to find the relevant files for the task at hand and start there. That instinct produces fast initial progress and slow everything after. You fix the surface problem without understanding the system it lives in, introduce a pattern inconsistent with what already exists, or create a dependency that breaks something you never looked at.
We read in a specific order. That order matters.
- The entry points.
Every Antigravity application has a defined set of external interfaces which are the points where the outside world reaches in. We mapped every API route, every webhook receiver, every event listener that the application exposed. Not the implementation of each one. Just the map. Twelve endpoints total, four of which touched the workflow processing layer.
- The data model.
The Prisma schema which is the file that defines every database table, every column, every relationship. We read it in full and drew the entity relationship diagram by hand. Three things stood out: the workflow_runs table had no foreign key constraint on the tenant_id column, meaning nothing in the database enforced that a workflow run belonged to a real tenant. The integration_configs table had a credentials column typed as plain JSON with no encryption marker. And there was a processing_queue table that had been added recently, based on the migration timestamp, with no index on the status column which would be the column every queue processor queried on.
- The agent orchestration layer.
Antigravity’s core value is its orchestration of AI agent workflows. We read every agent definition, every tool call, every handoff between agents. What we were looking for was not correctness but for assumptions. Where did one agent assume data from another would always be present? Where did a tool call have no error handling? Where was the retry logic? There were seven agent-to-agent handoffs. Four of them had no fallback for a failed upstream step.
- The infrastructure configuration.
Environment variables, deployment configuration, external service integrations. Two findings: the Stripe webhook secret was present in the production environment but not in the staging environment, meaning payment events had never been properly tested in staging. And the application had no rate limiting configured on any endpoint.
By hour four, we had a complete picture. Not a complete understanding of every function that takes weeks. A complete picture of the risk surface.
The Risk Map
Before writing a line of code, we produced a risk map. This document had three sections.
- What we could not touch.
Any part of the codebase that was working, tested, and not on the critical path for the integration deadline. We identified the core workflow execution engine, the tenant provisioning flow, and the billing integration as no-touch zones. Changes to these during a 48-hour sprint introduce risk that the sprint’s value cannot justify. If we found bugs in them, we documented them for post-integration remediation. We did not fix them.
- What we had to touch to deliver the integration.
The webhook receiver layer, the processing queue, the integration configuration storage, and the authentication middleware. These were unavoidable for the mTLS integration requirement.
- What was risky in the things we had to touch.
The credentials column storing plaintext in the integration_configs table meant we could not write a new integration config without either introducing a security regression or fixing the encryption first. The missing index on processing_queue.status meant any queue processor we wrote would produce a full table scan on the most frequently queried column. The missing tenant_id constraint meant our new integration endpoint needed to validate tenant ownership in application code, not rely on the database layer.

These three issues were not on the integration ticket. They were in the path of the integration ticket. Ignoring them would have produced a working integration built on a foundation with known structural problems. We scoped them as prerequisites, not additions, and communicated that to the CTO before writing any code.
The response was: “I didn’t know about any of those. How long does fixing them add?” The answer was four hours. They had been there for months. Four hours to fix. Nobody had looked.
Hour 4 to 8: The Four-Hour Prerequisite Block
Before the integration itself, we addressed the three structural issues the risk map surfaced.
- Encrypting the credentials column.
We added application-level encryption to the integration_configs.credentials field using AES-256-GCM, a symmetric encryption standard that is computationally fast and widely supported.
AES-256-GCM uses a key and an initialisation vector (a random value that ensures identical inputs produce different ciphertext) to encrypt data. The encryption key was added to the secrets manager and injected as an environment variable at runtime.
The migration to encrypt existing records required three steps: read every existing row, encrypt the credentials value, write it back. We ran this in a transaction, a database operation where either all changes succeed or none are applied, with a rollback tested in staging before touching production. Forty-seven existing rows. Transaction completed in under two seconds.
- Adding the processing queue index.
A single migration: CREATE INDEX CONCURRENTLY idx_processing_queue_status ON processing_queue(status). The CONCURRENTLY option allows the index to be built without locking the table, meaning existing queries continue to run while the index is created, rather than blocking until it’s complete. Critical on a table that the queue processor polls every five seconds.
- Adding the tenant_id constraint.
A foreign key constraint from workflow_runs.tenant_id to tenants.id. We added a data integrity check, a query to verify no orphaned records existed and then applied the constraint. Two existing records had null tenant_id values from early development. We resolved those with the CTO before applying the constraint.
Prerequisites closed. Integration work began at hour eight.
Hour 8 to 18: Building the mTLS Integration
Mutual TLS authentication works like this: when a client connects to your server, your server presents its certificate (as in standard HTTPS). Then the server asks the client to present its certificate. The server validates the client certificate against a list of trusted certificate authorities.
If the certificate is valid and trusted, the connection is allowed. If not, the connection is rejected before a single byte of application data is exchanged.
For this integration, the enterprise client provided their root certificate authority, the certificate that signed their client certificates. Every request from their internal systems would carry a certificate signed by this root CA.
The Antigravity application ran behind a Node.js HTTP server. Implementing mTLS required configuring the server to request client certificates, validate them against the provided root CA, and reject connections whose certificates failed validation. It also required extracting the client certificate’s Common Name field — the identifier embedded in the certificate and using it to route the request to the correct tenant configuration.
The implementation had four components:
- The TLS configuration layer.
Node’s https.createServer accepts a ca option like an array of trusted certificate authority certificates and a requestCert option that tells the server to request a client certificate. Combined with rejectUnauthorized: true, this produces a server that rejects any connection without a valid certificate from the trusted CA. We stored the root CA certificate as a base64-encoded secret and loaded it at server startup.
- The certificate extraction middleware.
Once the TLS handshake completes, the client certificate is accessible on the request object as req.socket.getPeerCertificate(). We wrote a middleware function, a function that runs before the route handler for every request, inspecting and potentially modifying it, that extracted the certificate’s Common Name, looked up the matching tenant configuration in integration_configs, and attached the tenant context to the request. If no matching configuration was found, the middleware returned a 401 before the request reached the route handler.
- The webhook receiver route.
The actual endpoint: POST /integrations/webhook/:integrationId. Responsibilities: validate the request body against the integration’s configured schema, transform the payload from the client’s format to the internal workflow event format, write the transformed event to processing_queue, and return a 202 Accepted response with a correlation ID which is a unique identifier attached to this processing job that the client could use to check status later.
The 202 rather than 200 was intentional. A 200 would imply the processing was complete. A 202 means “received and queued.” For an integration where processing could take several seconds and the client’s SLA required acknowledgment in under 500ms, the 202 with a correlation ID was the correct contract.
- The queue processor update.
The existing queue processor needed to recognise the new event type and route it to the correct agent workflow. We added one case to the existing switch statement, wired it to the Antigravity workflow trigger, and added structured error logging with logs formatted as JSON key-value pairs for both successful processing and failures. Failures wrote back to the queue row with a status of failed and an error message, which the client’s status endpoint could surface.
Hour 18 to 28: Testing Against the Enterprise Client’s Staging Environment
The enterprise client had a staging environment that could generate test webhook payloads signed by a test certificate from their CA. We had access to this environment as part of the engagement scope.
Testing a mutual TLS integration requires testing the full certificate exchange, not just the application logic. A common mistake is to mock the certificate in local tests and then discover in the real integration that the certificate’s Common Name format, the CA chain structure, or the certificate validation logic differs from the mock. We had learned this on a previous engagement. We tested against the client’s real staging infrastructure from the first test, not the last.
- First test: connection refused.
The CA certificate we had loaded had an intermediate certificate in the chain, a certificate that sits between the root CA and the client certificate that we had not included in the ca configuration. TLS validation requires the full chain. We obtained the intermediate certificate, added it, restarted the server. Connection established.
- Second test: 401 on every request.
The Common Name in the test certificate was formatted as service.internal.client.com, not the plain identifier we had expected. The tenant lookup was failing because the configuration record had been created with the expected format, not the actual format. We updated the configuration record and added a normalisation step to the middleware that strips domain suffixes from Common Names before lookup.
- Third test: 202 returned, queue row created, workflow triggered, result processed in 4.3 seconds. End to end.
We ran the test sixteen more times with varying payload sizes, a simulated slow response from the upstream API, and a simulated queue processor restart mid-processing. Every test produced the expected result.
Documentation took two hours: the architecture decision record explaining why we chose mTLS over API key authentication, the operational runbook for rotating the CA certificate when it expires, and the internal wiki page covering the new endpoint’s contract and error codes.
Hour 28 to 36: Production Deploy and Verification
Production deploy on an Antigravity application with a live queue processor required a specific sequence to avoid dropping events during the deployment.
We drained the queue first and waited for all in-progress items to reach a terminal state (completed or failed) before deploying. The processor was paused, not stopped: it continued to accept new items to the queue table but did not begin processing them. New webhook calls from any existing integrations continued to queue normally.
The deploy ran the migration for the credentials column encryption, the index creation, and the foreign key constraint in that order, each in its own transaction. Then the new application code was deployed. Then the queue processor was resumed.
First live request arrived seven minutes after the processor resumed: a test payload from the enterprise client’s production environment. Certificate validation passed. Payload transformed. Queue entry created. Workflow triggered. Correlation ID returned in 312ms.
The 48-Hour Timeline
| Time | Action | Output |
|---|---|---|
| Hour 0–4 | Cold read: entry points, data model, agent layer, infrastructure | Risk map: 3 structural prerequisites, 4 no-touch zones |
| Hour 4–8 | Prerequisite fixes: credentials encryption, queue index, tenant constraint | 3 migrations deployed to staging, verified, documented |
| Hour 8–14 | mTLS server configuration + certificate extraction middleware | Server accepting and validating client certificates in staging |
| Hour 14–20 | Webhook receiver route + queue processor update | Full payload flow from receipt to workflow trigger, staging |
| Hour 20–28 | Testing against enterprise client staging: 18 test runs | Full certificate chain issue resolved, Common Name normalisation added |
| Hour 28–30 | Production deploy sequence: drain, migrate, deploy, resume | Live environment running new code with all three prerequisite fixes |
| Hour 30–36 | Production verification: first live request, edge case validation | Integration live, first real payload processed end to end |
| Hour 36–42 | Documentation: ADR, runbook, API contract, internal wiki | Four documents handed to team |
| Hour 42–48 | Post-launch monitoring, edge case handling, team walkthrough | Team able to operate, maintain, and extend the integration independently |
What the Codebase Told Us That Nobody Knew
When you read a codebase cold, you read the history of every decision that was made and every decision that was deferred. Antigravity generates excellent code for the feature in front of it. What it cannot generate is the contextual decision-making that happens when a senior engineer encounters a problem: should this data be encrypted at rest, or is column-level encryption sufficient? Should this queue use a database table or a dedicated message broker? When this index is missing today, what is the query pattern going to look like in six months?
Those decisions were deferred in this codebase. Not recklessly. They were deferred the way almost every agentic codebase defers them: because the generation moved faster than the architecture, and the architecture never caught up.
What we found in 24 hours of reading:
| Finding | Where | Risk Level | Discovery Method |
|---|---|---|---|
| Plaintext credentials in integration_configs | Database schema | Critical | Schema read, hour 1 |
| Missing tenant_id foreign key constraint | Database schema | High | Schema read, hour 1 |
| Unindexed status column on processing_queue | Database schema | High | Schema read + migration review |
| 4 of 7 agent handoffs with no failure fallback | Agent orchestration layer | High | Agent definition read, hour 3 |
| No rate limiting on any endpoint | Infrastructure config | High | Config read, hour 4 |
| Stripe webhook secret absent from staging | Environment config | Medium | Config comparison, hour 4 |
| No structured logging in queue processor | Application code | Medium | Code read, hour 3 |
| No certificate rotation runbook | Documentation | Medium | Documentation audit, hour 4 |
The CTO had been operating this system for four months without knowing any of these existed. That is not negligence. It is the natural result of building with agentic tools without a senior engineer auditing what the tools produce.
Every item in that table except the last two was present in the codebase before we arrived. Every item in the table is now documented, prioritised, and either fixed or on the post-integration roadmap.
Four Things We Would Do Differently If We Had Been There From the Start
This section is not retrospective criticism. It is the practical output of working backwards from the 48-hour embed to understand what architectural decisions, made earlier, would have made the integration faster, safer, or both.
- Credentials encryption from the first record.
The integration_configs table was created to store integration settings. The first engineer to add a credentials field to it should have raised the question: does this need to be encrypted at rest? The answer, for any field that holds authentication material, is always yes. Adding encryption after 47 records are live requires a migration with a rollback path. Adding it on row zero is four lines of code.
- The processing queue as a first-class infrastructure decision.
Using a database table as a message queue is a reasonable choice for a system at early scale. It is a decision that should be made explicitly, documented, and sized against the expected event volume. The processing_queue table had no documentation of why it was implemented this way, no capacity analysis, and no migration plan for if and when database-backed queuing would need to become a dedicated message broker. The decision itself was defensible. Its absence from any architectural decision record meant the next engineer to touch it (us) had to reverse-engineer the intent.
- Structured logging from day one.
Adding structured logging to the queue processor took forty minutes during the integration sprint. It would have taken forty minutes on day one of the build. The difference is that structured logs from day one would have given this codebase four months of searchable, queryable operational data. Instead it had four months of console output that is useful for debugging a development session and nearly useless for diagnosing a production incident.
- A security audit at the first external integration.
The first time any Antigravity-built application accepts data from an external system, a senior engineer should review the security model of that integration: how is the caller authenticated, what data is it allowed to send, where does that data land in the database, and what access controls protect it. That review would have caught the plaintext credentials column, the missing tenant constraint, and the absent rate limiting at the moment they were created, not four months later under a 48-hour deadline.
None of these are exotic engineering practices. They are the habits of engineers who have shipped production systems before and know which shortcuts compound.
What “Embedded From Day One” Actually Means
The 48-hour sprint worked. The integration shipped. The enterprise client’s first production payload was processed cleanly on a Sunday night, and by Monday morning the CTO had verified the full end-to-end flow with their client’s team.
But the sprint also produced a list of eight findings that were present in the codebase before we arrived. Four of them were high or critical severity. None of them were part of the integration scope. All of them will need to be addressed before this product can be considered production-hardened.
The 48-hour embed resolved the immediate crisis. The four months of agentic development before it accumulated structural risk that will take several weeks of targeted work to fully remediate.
That is the honest picture of what late-stage senior engineering embed looks like. It is valuable. It is faster than most CTOs expect. And it is more expensive than having a senior engineer in the architecture conversation from the first sprint.
“From day one” does not mean a senior engineer writes every line of code. Antigravity is generating code faster than any human team could write it, and that speed advantage is real and worth keeping. “From day one” means a senior engineer is in the room when the architectural decisions are made, reviewing what the agentic tools produce against those decisions, and doing the structural work like the credential encryption, the queue design, the logging strategy, the security model that code generation does not do by default.
The product you are building with Antigravity is good. The architecture it runs on is the part that determines whether it scales.
Your agentic stack is only as good as the engineering behind it.
If Your Situation Is Closer to the Integration Sprint Than the Full Embed
Not every team reading this is four months into an Antigravity build with a critical deadline in 96 hours. Some are earlier at the stage where the architecture decisions are still being made, and embedding a senior engineer now would prevent the structural issues rather than remediate them.
Some are working with a different kind of agentic stack, or with AI-assisted development tools at the sprint level, where the question is not architectural embed but targeted remediation of specific technical debt.
And some are working on a no-code or low-code foundation that has its own production-readiness gaps below the agentic layer gaps that need a different kind of intervention than the one described in this post.
The right engagement depends on where you actually are. Feel free to book a free session with us and let us help you out with your AI generated application.

Keyur Patel
Co-Founder
Keyur Patel is the director at IT Path Solutions, where he helps businesses develop scalable applications. With his extensive experience and visionary approach, he leads the team to create futuristic solutions. Keyur Patel has exceptional leadership skills and technical expertise in Node.js, .Net, React.js, AI/ML, and PHP frameworks. His dedication to driving digital transformation makes him an invaluable asset to the company.
Related Blog Posts

How to Build an AI Influencer Platform in 2026: The Next Frontier for Brands, Creators, and Digital Product Teams

Supabase Row Level Security: Everything You Need to Know

