← Home

You, AI and a Close Deadline

Illustration

This document shares my recent journey and insights from leveraging Large Language Models (LLMs) as a coding co-pilot, particularly in the context of integrating new vendor management features into an existing system. My focus has been on how to truly maximize efficiency, rather than just pushing harder, by embracing AI in my development workflows. I’m certainly no expert in this field; I’m simply sharing what I’ve learned through my experience after embracing AI as a co-pilot, especially with the ZIP integration, to speed myself up.

Context: For a while, I hadn’t held a very positive opinion about AI in coding. It wasn’t a philosophical opposition, but I struggled to see where it fit into my established workflow of macros, custom key mappings, and shortcuts. AI as a boilerplate generator didn’t immediately resonate, as I already had automation for that.

However, my recent experience with ZIP integration changed my perspective. We aimed to bring this integration into Sprinto, introducing two fundamentally new concepts: a proper vendor management suite into the existing models and the ability to PUSH data out. Before diving into ZIP, I warmed up with other integrations like Gitlab Vulnerabilities, Sentinel One, and Grafana, as I hadn’t touched integration code in over two years.

Typically, my delivery per integration took around two weeks. Much of this time was spent deciphering undocumented code and managing a high cognitive load, especially with the constant discovery of new code patterns across different integration modules. My teammates were already optimizing the integration data layer, and their prompt-writing patterns really enabled me to accelerate and deliver much quicker with AI’s help.

Cognitive Load: Pre-Post AI

Before AI, the initial phase of any integration required a lot of context-gathering—understanding how things worked, tracing patterns, and building a mental map. I’d eventually hit a point midway where things started to click and I could breathe a bit—but that was usually short-lived, as I’d soon realize the module needed to be connected to many other moving parts.

Cognitive load, pre-AI vs post-AI

With AI in the mix, the overall cognitive load is noticeably lighter right from the beginning. That’s partly because I already had some context, but more importantly, the prompts themselves started acting like reference checklists—helping me quickly validate what needed to be done and keeping my mental overhead low.

Cognitive load over time

Previously, I’d tend to hoard a lot of information in my head. Since I could mentally visualize much of the code, it helped me move faster—but it also meant I had to power through long, focused coding sessions and be ready to handle all the tricky edge cases later on.

With AI, execution became more prototypical. Instead of writing everything upfront from zero to one, I’d ask Claude to generate scripts and populate models, allowing me to jump directly into building post-connections—skipping the UI layer entirely at first. Claude also helped me explore a few different implementation designs without sacrificing speed, which was especially valuable under a tight deadline.

The Good: Where LLMs Shine

Fundamentally, LLMs are pattern recognizers. They excel at memorizing our patterns and producing results based on them in a non-deterministic way.

Illustration

Importance of Well-Defined Patterns: I was initially skeptical about using AI for new flows, anticipating extensive code auditing. However, because our “Flows” are so organized in terms of definition, inputs, and outputs, it’s easier for me to evaluate correctness and for the AI to recognize and execute patterns. For instance, our temporal flow already expects a particular directory structure, specific file names, and certain exports, with steps defining their inputs and outputs. This structured approach has been a massive step up, though I still see a need for more human-readable error handling for expected prerequisites, making issues more actionable from the get-go.

The Bad: The Pitfalls of AI Assistance

In certain scenarios, I’ve observed that LLMs are not without their drawbacks, especially when I haven’t guided them meticulously.

Picking Up Bad/Deprecated Patterns: One of the most annoying challenges is the LLM’s tendency to reproduce existing patterns, even if they are bad, deprecated, or incorrect. This happens because the pattern is easily observed by the AI, with no inherent indication of its current state or validity (e.g., consistently mapping entries in actions.js when it’s no longer necessary). Overcoming this requires very explicit instructions. It highlights my responsibility to recognize and correct such patterns, perhaps by adding more examples, updating instructions, re-writing prompts, or doing it manually. It also emphasizes the need for consistent and willful adoption of new patterns across our codebase, preventing mini-workarounds from spreading.

“Going Crazy” (Extended Exploration): Sometimes, the AI can take 20 minutes to explore without useful output. This waiting is unproductive and disrupts my focus. It suggests setting hard constraints on AI interactions, perhaps by breaking down requests into smaller chunks.

Illustration

Reviewing a lot of code quickly builds tunnel vision: A prompt doesn’t always guard an LLM from not doing something. I frequently found myself missing simple cases when generating a lot of code that otherwise looked correct. For instance, in our database model, we define ENUMs, but in migrations, we create them as Strings. Claude, however, kept writing ENUMs in both places. While this is something we can avoid with better prompts, the key thing is that small discrepancies like these can easily slip through when the rest of the generated code appears good enough.

Incorrect (the agent kept emitting ENUM in the migration too):

provider: {
  type: DataTypes.ENUM(['GSUITE', 'ZIPHQ']),
  allowNull: false,
},

Correct:

// model
provider: {
  type: DataTypes.ENUM(vendorProviders),
  allowNull: false,
},

// migration
provider: {
  type: DataTypes.STRING,
  allowNull: false,
},

The Ugly: The Debugging Nightmare

Illustration

The most counterproductive aspect I’ve identified has been debugging AI-generated code.

Increased Debugging: In parts of the codebase where data flow is unclear or side effects are buried, asking AI to generate changes doesn’t inspire immediate confidence—because you’re not entirely sure what’s going on either. You end up validating AI output line by line, not because you distrust the model, but because the surrounding context is shaky to begin with.

The problem is, debugging in these areas is already hard. So instead of saving time, AI ends up compounding the effort—I spend more time untangling assumptions, tracing props, and double-checking whether something “might” work, which defeats the point of using it in the first place.

For instance, I’d find myself asking:

Opportunity in Contextual Understanding: LLMs, being non-runtime tools, can’t inherently validate code against runtime failures or complex business logic—unless we explicitly provide patterns for validation (like form validators or assertions). This isn’t a limitation of the LLM itself, but a reflection of how we interact with it. The opportunity lies in improving the context we feed it: the clearer our guardrails and expectations, the better the model performs.

In our frontend, for example, we often take a small unit of value, apply multiple transformations, and render it in the UI. But at no point do we assert that the input matches what we expect. Take integrationCategoryMap—it assumes the category is present as a precondition for the integration, but there’s no code-level assertion to catch violations. Without these signals, LLMs have no way to understand what must be true for the flow to succeed.

We can work around this by writing prompts carefully—but natural language alone isn’t a spec. It’s not enough to define how APIs or components are meant to interact. Claude (and others) can pick up some of these expectations if the code and patterns make them explicit. Without that, prompting alone falls short.

Getting the Schematics Right: While AI helped me get around things quickly, the way the code was written was often unsettling and questionable to my taste. I ended up rewriting some of it and tweaking many more places to get it right. I think this is something that will be solved pretty quickly as we develop more closely coupled patterns. However, it also takes away an engineer’s ability to pause and question their code. I’m already seeing this in many places: things could have been written in a simpler and saner way, but getting to the result became much more important. The problem with this is that it just creates a large piece of code from an unopinionated engineer, which takes away the questioning and reasoning of doing something or writing something a certain way.

Lessons Learned and Moving Forward

Think, build, standardize, automate

In my experience, for maximizing LLM efficiency in coding: