{"slug": "how-i-directed-an-ai-agent-through-3-real-architecture-decisions-and-what-i", "title": "How I Directed an AI Agent Through 3 Real Architecture Decisions, and What I Learned", "summary": "A developer built Retro Dynamics Agent, an app that generates and facilitates retrospective activities, using the AI coding agent Claude Code for most of the design, implementation, and debugging. The project involved three key architecture decisions, including using OAuth for Jira integration with a signed HMAC state parameter, and opting for Liveblocks over Supabase Realtime for the collaborative board. The developer emphasized the importance of human oversight in verifying production behavior, as they found a critical bug in production logs that code review missed.", "body_md": "In two weeks, I built Retro Dynamics Agent, an app that generates retrospective activities for teams, facilitates them on a real-time collaborative board, and turns the outcomes into Jira or Azure DevOps tickets.\n\nI built it working with an AI coding agent, Claude Code, throughout almost the entire process: design, implementation, production debugging, and documentation.\n\nI do not want to tell another “I used AI and it wrote the code for me” story. We have heard that one enough.\n\nWhat I found more interesting were the parts of the project where there was no obvious answer in a tutorial, and how the work was divided in those situations.\n\nI defined the constraints and made the underlying decisions. The agent proposed concrete technical solutions and implemented them. Then the responsibility for verifying that everything actually worked, not just that it compiled, came back to me.\n\nHere are three examples from the project.\n\n1.- Connecting to Jira without server-side sessions or frontend memory\n\nI wanted any team to be able to connect its own Jira account through OAuth, instead of relying on a global token that only I could configure.\n\nThe problem was that my application runs entirely on serverless functions. Nothing stays in memory between requests, and the frontend does not maintain its own state either.\n\nNo localStorage. No router.\n\nAn OAuth login means leaving the application, authenticating with Atlassian, and then coming back.\n\nBut coming back to what, if nothing remembers which screen you were on?\n\nBefore touching the code, I asked the agent to create a complete implementation plan, including the files that would need to change, the design decisions, and the scope.\n\nI reviewed that plan as if it were a pull request from another developer.\n\nI made decisions such as:\n\nFor now, only Jira would use OAuth. Azure DevOps would keep its manual token flow because setting up OAuth there is considerably more involved.\n\nTokens would be encrypted before being stored in the database, never saved as plain text.\n\nAnd the existing manual connection form would remain available as a fallback instead of being removed.\n\nThe agent solved the specific technical problem by proposing a signed HMAC state parameter.\n\nBesides validating the OAuth callback, that state contains the ID of the active session.\n\nBecause the frontend does not remember anything on its own, carrying that session ID through the OAuth round trip is what allows the application to restore the correct screen when the user returns from Atlassian.\n\nI verified the implementation myself in production.\n\nThat is where I found a bug that no code review would have caught.\n\nAdding a single dependency brought down the entire application, including the health check.\n\nThe reason was that Vercel was installing dependencies using a different lockfile from the one I thought it was using, even though a comment in my own code had been claiming the opposite for months.\n\nI found the problem by reading real production logs in the dashboard, not by analyzing the code.\n\n2.- The real-time board: why I did not use the database I already had\n\nThe board needs to synchronize note dragging, votes, and participant cursors in real time.\n\nI already had Postgres through Supabase, so using the same provider’s real-time system would have been the simplest option from an integration perspective.\n\nNo additional synchronization provider. Less infrastructure.\n\nI decided not to use it.\n\nThe board is free-form, closer to Miro than to a fixed-column board.\n\nFor that kind of canvas, Supabase Realtime would have required me to implement more synchronization and conflict-handling logic than I wanted to own.\n\nWith free dragging, live cursors, and concurrent edits, two people trying to interact with the same object at the same time is not an edge case. It is something that will happen regularly.\n\nLiveblocks is specifically designed around collaborative canvas-style experiences.\n\nI accepted the cost of maintaining two providers rather than building and maintaining that synchronization layer myself.\n\nThe agent proposed the synchronization model:\n\nLiveblocks is the source of truth while the session is active.\n\nWhen the session ends, the final state is persisted to Postgres in a single operation instead of writing to the database on every movement.\n\nThe agent also corrected an assumption I had made early on.\n\nI originally thought notes and groups needed separate database tables.\n\nThey did not.\n\nThey are generated and consumed together, never independently, so separating them would have introduced complexity without giving me any real benefit.\n\nVerifying this architecture required more than reading the code.\n\nAt that point I did not have a convenient real Liveblocks environment available to test interactively from the browser.\n\nSo we created a script that connects two real Liveblocks clients without a browser.\n\nOne client makes a change.\n\nThe second client must receive it immediately.\n\nThat test validated the architecture better than any code review could.\n\n3.- Voting without people accidentally overwriting each other\n\nDuring the voting phase, any participant can vote for a note or remove their vote at any time.\n\nSeveral people can do this simultaneously.\n\nIf the vote total were stored as a single shared counter, concurrent writes could overwrite each other.\n\nI decided that voting should be reversible and unlimited per participant.\n\nThat sounds like a product decision, and it is.\n\nBut it completely changes the data model.\n\nInstead of everyone updating the same counter, each participant writes only their own list of voted notes.\n\nThe total for a note is calculated by counting how many participant lists contain that note.\n\nNo two participants ever compete to write the same value.\n\nInstead of solving a concurrency conflict, the model removes the conflict altogether.\n\nWhen the session closes, only the final vote count is persisted. The system does not keep a record of who voted for what.\n\nThe counting logic was tested as standalone functions with no dependency on Liveblocks types.\n\nThat means the correctness of the calculation can be verified without needing a real-time connection running at all.\n\nWhat kept repeating across all three decisions\n\nThe valuable part was never that the AI could write the code.\n\nThe same pattern appeared every time.\n\nBefore implementing any non-trivial feature, we discussed the architecture first.\n\nI defined the constraints, chose which trade-offs were acceptable, and decided which risks I was willing to take.\n\nThe agent was consistently good at proposing and implementing solutions within those constraints.\n\nBut my work increasingly moved away from translating decisions into syntax and toward deciding what to build, which trade-offs to accept, and what evidence I needed before I could say that something actually worked.\n\nIn the OAuth integration, that evidence came from production logs.\n\nIn the real-time architecture, it came from two real connected clients.\n\nIn the voting model, it came from isolated functions that could be tested independently.\n\nThe more capable coding agents become, the less interesting “who wrote the code?” becomes as a question.\n\nThe more interesting question is:\n\nWho defined the constraints, chose the trade-offs, and decided what counted as proof that the system worked?\n\nEverything from this project is documented in the repository, including the production incident and the root cause once I found it.\n\nGitHub - alejandraarochaovalles/retro-dynamics-agent\n\nContribute to alejandraarochaovalles/retro-dynamics-agent development by creating an account on GitHub.\n\ngithub.com\n\nAlso, here is the link to try the application in production: [https://retro-dynamics-agent-gcdg.vercel.app/](https://retro-dynamics-agent-gcdg.vercel.app/)", "url": "https://wpnews.pro/news/how-i-directed-an-ai-agent-through-3-real-architecture-decisions-and-what-i", "canonical_source": "https://dev.to/alejandraarochaovalles/how-i-directed-an-ai-agent-through-3-real-architecture-decisions-and-what-i-learned-1p08", "published_at": "2026-09-07 12:29:35+00:00", "updated_at": "2026-09-07 12:57:49.593210+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-products"], "entities": ["Claude Code", "Retro Dynamics Agent", "Jira", "Azure DevOps", "Liveblocks", "Supabase", "Vercel", "Atlassian"], "alternates": {"html": "https://wpnews.pro/news/how-i-directed-an-ai-agent-through-3-real-architecture-decisions-and-what-i", "markdown": "https://wpnews.pro/news/how-i-directed-an-ai-agent-through-3-real-architecture-decisions-and-what-i.md", "text": "https://wpnews.pro/news/how-i-directed-an-ai-agent-through-3-real-architecture-decisions-and-what-i.txt", "jsonld": "https://wpnews.pro/news/how-i-directed-an-ai-agent-through-3-real-architecture-decisions-and-what-i.jsonld"}}