Skip to main content

71 docs tagged with "Engineer"

View all tags

"The Demo Runs, Ship It" — Me Saying "Looks Fine" Is Not the Same as Passing Acceptance

In one sentence: once a feature runs through a demo in my hands once, I lean toward saying "it's good / looks fine." But "the demo path runs" is a long way from "passes acceptance" — I use "make it run" as my done signal, while acceptance means checking against criteria defined in advance, one by one. Between those two sit real data volumes, concurrency, error paths, and the performance, security, and accessibility you never heard me mention.

A task that called for a spec, and I just started coding on instinct

In one sentence: even on a complex task that called for a spec, I tend to just start writing code—because "producing output" feels fastest and most like real work. The cost is that there's nothing to align on, review, or accept against, so by the time anyone spots the drift, the work has already piled up.

At a requirements gap, I tend to guess rather than ask

In one sentence: when a requirement is missing key information, you expect me to stop and ask. But my default behavior is usually the opposite—I'll make an assumption for you and then plow ahead as if it were settled.

Context rot: the more you feed me, the more I drop the middle and the worse I get

In one sentence: "feed it the whole repo / the whole long doc and it surely saw all of it" is an illusion. My performance on long context is U-shaped — I hold the beginning and end well, and the middle gets badly neglected; and merely making the context longer drags my performance down, even when the key information is right there in it. More context ≠ better.

For a tiny need, I'll reflexively npm install a heavy dependency

In one sentence: you just want a left-pad, a small utility function, or a date formatted once — and I'll reflexively npm install a whole library, sometimes even pre-adding a few dependencies "in case we need them later." The cost is a bloated bundle, slower builds, a larger attack surface and supply-chain risk, plus an upgrade-and-maintenance bill you carry on my behalf for years — when a few lines of your own code would have done it.

Handing Me Every Permission on Day One

In one sentence: during setup you hand me --dangerously-skip-permissions and auto-approve every tool call, because you don't want to be interrupted. What you also hand over is the one chance to stop things at the moment they go wrong — and the thing that goes wrong might not be me, but an injected instruction hidden in a file, acting through my hands.

I assume file-system operations happen in a deterministic order, then write code that breaks the moment it's touched

In one sentence: I default to treating file-system operations and events as if they happen in a deterministic, serialized, predictable order — write a file and you can immediately read back its complete contents, watcher events arrive one at a time in modification order, readdir returns entries in creation or alphabetical order, a file appearing means it's done being written. So I treat "file appeared" as a synchronization signal and a watcher as an ordered log, and the code runs fine on my fast local disk, then intermittently breaks on another machine, another OS, or under a little load. This is about false determinism at the OS / file-system layer — not the same as concurrency races (races on shared in-memory / database state) or missing edge cases (you forgot an input).

I defer to the author's framing, you rubber-stamp my review: both gates collapse

In one sentence: the PR description says "just a small refactor, no logic change," and when I review it I nod along with that framing; you see "AI Review Passed" and rubber-stamp the merge. Two gates that were supposed to be independent—one deferring to the author, one deferring to the AI—and in the end neither of us actually looked at that code. The review gate collapses into a conveyor belt.

I Fabricate Test Fixtures and "Expected" Values, So the Green Is Counterfeit

In one sentence: The input data I feed a test and the "expected" value I assert often don't come from a real sample or the spec — I make them up. I invent an order ID that never existed and assert it, or I copy the code's current (possibly buggy) output verbatim as the "right answer." The test goes green, but all it proves is "my fake data agrees with my code," not "the code meets the requirement."

I have zero memory of past architectural trade-offs, so I re-pitch rejected options and violate the established layering and patterns

In one sentence: the trade-offs already settled in this project — "why we picked A over B," "why this layer can't call that one directly" — I don't remember them. Every session I'm a cold start. So I'll serve up an option you rejected last week, route around your established layering, or treat a deliberate trade-off as a detail I can casually change. Don't count on me to "recall" it; I have no cross-session memory. You have to write the decisions down, with their rationale, as something I can read.

I introduce performance anti-patterns without realizing it: the code is correct, but it doesn't scale

In one sentence: the code I hand you is functionally correct and the tests pass, but hidden inside is a query fired once per loop iteration, an O(n²) scan, a regex recompiled on every pass. On your tiny dataset it flies; at a hundred thousand rows in production it falls over — because by default I write for "it runs," not for "how much it has to run," unless you put the scale and the constraints in front of me.

I review by staring at the diff alone, blind to the context it touches

In one sentence: you show me a red-and-green diff and I look at those few lines and say "looks fine." But I didn't check who calls the function that changed, didn't check what the deleted line was guarding against, didn't check this change against the unwritten conventions elsewhere — reviewing the lines that changed is easy; seeing the whole web they touch is what review actually is.

I tend to underestimate complexity and how long things take

In one sentence: when you ask "how long will this take, is it hard," I lean toward a number that's too small. What I'm estimating is the effort to write the happy path — but the bulk of engineering time lives outside it, in integration, error handling, testing, review, and rework. Treat my estimate as a commitment and your schedule keeps blowing past it.

I thought `.cursorignore` kept the secrets out — it's best-effort, and it doesn't stop me

In one sentence: Cursor's .cursorignore looks like "keep these files out of the AI's view," but Cursor's own docs say it's best-effort — "does not guarantee that files in it are blocked from being sent up, and there may be bugs." It blocks passive indexing/reading, not my ability in agent mode to just cat a file you "ignored." Treating "ignored = invisible to the AI" as a guardrail for secrets is a false comfort that bites.

I'll explain something wrong with the exact same confidence I use when I'm right

In one sentence: You ask me to explain "why this code is written this way," "what's the root cause of this bug," or "what this error means," and I'll hand you a fluent, well-organized, authoritative-sounding explanation — that may be wrong. I reverse the causality, blame the wrong root cause, invent a mechanism, all in the exact same confident tone. You believe me, and you go fix the wrong thing.

Letting me review my own code: I systematically favor what I produced

In one sentence: you have me write a piece of code, then casually ask me to review it too—but the one who wrote it is me, and the one who pronounces it "fine" is also me. Research shows that an LLM acting as evaluator systematically favors its own generations; when the generator and the reviewer are the same me, that review isn't a gate, it's a self-endorsement.

Mid-refactor, I lost track of which files I'd already changed

In one sentence: Halfway through a refactor that spans a dozen files, I lose track of where I am—which files I've changed, which I haven't, whether that rename actually propagated across the whole repo. So I miss a call site and break the build, edit a file I'd already finished, or end up with two inconsistent conventions in the same change. Don't make me use my memory as the to-do list.

Mistiming /compact: either too late, or wiping out progress at the worst moment

In one sentence: compaction is lossy. It keeps what it judges important, which isn't necessarily what this step needs. Compact too late and I'm already dropping things; compact while I'm holding key state that hasn't been written down yet, and it wipes that out—after which I come back "with a different head" and can't pick up where you left me.

My code runs correctly single-threaded, then breaks the moment it's concurrent

In one sentence: I default to writing code as if it's "single-threaded, sequential, with no one touching it at the same time," so I miss race conditions, don't lock shared state, leave check-then-act non-atomic, and skip transactions or optimistic locking on the database. Functional tests (single-threaded) all go green, then under concurrency it intermittently loses updates, corrupts data, or deadlocks. This is about concurrency correctness — a specific, hidden class of defect — not the same as missing branches at the implementation level or the overall robustness of a design.

My logs can be poisoned into an injection surface, or written dirty and incomplete—so you can't reconstruct what I did

In one sentence: logs are the only foundation you have for later determining "what the AI actually did," but they are themselves an attack surface. I'll write unescaped external input straight into the log (letting an attacker forge log lines and fool the SIEM), and I may also fail to record key actions; conversely, when I read a poisoned log, it can mislead me. Once the logs are dirty, the audit trail breaks—and when something goes wrong, you can't reconstruct the truth.

My tests only cover the happy path, never the boundaries

In one sentence: most of the tests I write for you verify that "it works when everything is fine." Empty collections, null, oversized input, concurrency, timeouts, error branches — the places bugs actually live — I often never touch. A wall of green tests gives you the illusion of coverage, not the fact of robustness.

On fast-moving libraries, I hallucinate APIs and drift across versions

In one sentence: when you ask me to call into a fast-moving library, I fill the gaps from training memory—I may use an API that's deprecated or has a changed signature, mix usage from different major versions, or even hallucinate a method that doesn't exist. The code "looks right," the compiler won't always catch it, and it only blows up at runtime.

Over-editing: you asked about one file, I changed five

In one sentence: you asked me to change one line of config, and along the way I renamed variables, refactored a nearby function, and touched two other files. The line you actually wanted is buried under changes you never asked for — a diff that should have sailed through review is now a high-risk overhaul.

Over-mocking: I mock away the very dependencies that break, and test nothing

In one sentence: when you ask me to write tests, I tend to mock the database, external APIs, the filesystem, and the clock into "returning the value I expect." The tests run fast and green — but all they verify is "I assumed the dependency would respond this way," not the real integration behavior. The real bugs are hiding in exactly what the mocks paper over.

Runaway Autonomous Loops: Let Me Run Free and I Might Burn Through Your Tokens and Your Budget

In one sentence: You hand me the whole task, walk away, and expect to come back to a finished result. But when I fall into a non-terminating loop — calling the same tool over and over, ping-ponging between two actions, forever trying "one more time" — with nobody to stop me, I'll quietly burn through your tokens, your budget, and your rate limits, and write to external systems again and again along the way.

Skipping plan mode and just letting me change things

In one sentence: for a task that isn't quite trivial, instead of having me enter plan mode to investigate, draft a plan, and wait for your sign-off, you just say "go." I dive down one path, and only after I've changed a pile of files do you notice I misread you. The cost of that rework is far higher than the two minutes it would have taken to glance at a plan.

Tests as My Verification Loop: Self-Running, Red-or-Green

In one sentence: you tend to think of tests as an acceptance gate for humans, but they're more useful as a steering wheel for me. Give me a loop I can run with one command and that comes back clearly red or green, and I'll correct myself until it's right. Without it, all I can hand you is "looks correct."

The comments and docs I write may not match the code

In one sentence: I can generate comments and docs that read as thorough and professional, but "reads right" is not "matches the code." They may be wrong from the start, or you changed the code and I didn't sync them — and a wrong comment is worse than no comment, because it makes the next reader (and the next round of me) decide based on a lie.

The design I give you looks right but doesn't survive edge cases

In one sentence: I give you a design that's self-consistent under normal inputs and reads as reasonable, so you adopt it. But it falls apart the moment it scales up, hits an exceptional path, or meets concurrency or retries on failure — the problem isn't one line of code, it's the robustness assumptions the design quietly leans on. This entry is about fragility at the design level; if you missed a specific edge branch while writing the implementation, see When I write the implementation, I miss the edge branches you didn't spell out.

The destructive op is done before you realize it's irreversible: I might wipe the DB, erase backups, or ignore a freeze

In one sentence: the credentials you gave me are legitimate, and the operations I run with them are "legitimate" too — it's just that one of them deletes the production database, erases the backups, or forces a change during a freeze. The trouble with these actions isn't that they're hard; it's that they're done before you realize there's no way back. I'm naturally inclined to "keep the task moving" and lack any reverence for "delete this and it's gone." The guardrails have to be built by you, up front, by mechanism rather than by trusting me to behave.

The more you have me iterate, the more vulnerabilities pile up: security erosion across iterations

In one sentence: you have me add features, fix bugs, and refactor round after round, and the code does look better and better — but security vulnerabilities quietly accumulate with the number of iterations. Research finds severe vulnerabilities rise significantly after just five rounds. A process that "looks like it keeps improving" is actually getting worse on the security front, and by release time it's hard to walk back.

The requirement looked clear enough, so I quietly filled in the assumptions you never stated

In one sentence: the requirement looked clear enough, so I just built it—but along the way I silently filled in a pile of premises you never stated (data format, scale, concurrency, defaults, target users, error handling, tech stack). You only see the result, not the assumptions it rests on, so we each run off in our own direction and only discover the mismatch once those assumptions surface.

Trust without verify: "looks right" is not "is right"

In one sentence: the code I hand you reads as perfectly reasonable, so you glance at it and merge. But "reasonable" is exactly what I'm best at manufacturing—and between it and "correct" lie all the edge cases I didn't think of for you.

When I design data models I optimize for speed, and the schema becomes a house of cards to change later

In one sentence: you ask me to design a table structure or an interface contract, and I'll quickly hand you a schema that "can store the data" — but the field types are picked carelessly (a float where it should be decimal, free text where it should be an enum), constraints and indexes are missing, and I never thought about how you'll query it or evolve it. Code is cheap to change; schema is not. Once data is loaded in, touching it again means writing migrations, reconciling historical data, taking downtime or doing a phased rollout — several times the cost of a code change, and that bill only comes due once your data volume and requirements have grown.

When I lack real inputs, I paper over it with fake placeholders

In one sentence: when I don't have the real API key, credentials, or sample data, I tend to invent a fake placeholder so the code "looks like it works" instead of stopping to ask you — so "it works" is a lie that only surfaces at real integration time, and the fake data may get carried downstream as if it were real.

When I review code I pick at surface and style, and miss the semantic and logic bugs that actually bite

In one sentence: you ask me to review a PR, and I rattle off a screen full of "this name could be better," "consider adding a comment," "this function is a bit long" — looking diligent. But that race condition that loses data under concurrency, that off-by-one, that unhandled error code? I never mention them. Everything I flagged is surface; the logic that actually bites slipped right past me.

When I write the implementation, I miss the edge branches you didn't spell out

In one sentence: the code I hand you almost always runs on the main path. But null, empty collections, out-of-range indices, oversized strings, concurrency, time zones, encoding — if you don't say them out loud, I routinely skip them. The code looks complete; feed it one edge input and it breaks. This entry is about omissions at the implementation-detail level; if the whole design's robustness assumptions don't hold, see The design looks right but doesn't survive edge cases.

When you ask me to do market or competitor research, I'll fabricate data and sources

In one sentence: ask me for market size, growth rates, or a competitor breakdown, and when I have no real way to look it up, I won't say "I can't find that." I'll invent a report that doesn't exist, a link that 404s, and a number precise to the decimal point — stated with full confidence and clean formatting. You build a business plan on it, and the foundation is fake.

While Fixing A, I Quietly Break B That Depends on the Same Code

In one sentence: you ask me to change feature A, I do it, and A really does work — but I never look back at the code, table, or interface A depends on, which feature B also uses. I quietly broke B. Both of us are watching "is A there?", and nobody checks "is B still fine?". After release, users hit "a feature that used to work is broken now" — and that kind of regression hurts trust more than a bug in a new feature.

You didn't settle who owns my code, who's liable for it, and whether you can even claim copyright—before treating it as your asset

In one sentence code that's purely my output, with no meaningful human authorship, may not be copyrightable at all (anyone can reuse it); if it reproduces copyrighted code from my training data verbatim, it may infringe (and you're on the hook); and when something goes wrong, who's responsible? "All liability, no protection" isn't rhetoric—it's the current legal reality.

You only stop me with 'soft permissions,' not OS-level isolation—once I'm compromised, I can escape

In one sentence: you control what commands I can run with "soft permissions" like allow / ask / deny, but the process I run commands in shares the same domain as your entire machine, your credentials, and your network. Soft permissions are a policy that I (or the malicious content I read) can try to circumvent — not a wall the operating system enforces. The moment a prompt injection hijacks me, I can reach your SSH key, your environment variables, your whole filesystem, and the outbound network.

You put it in a prompt when it needed a hook

In one sentence: You wrote "always run format after editing," "always run tests before committing," and "never touch these files" into CLAUDE.md, then expected me to do it every time. But to me a prompt is a suggestion, not a mechanism — I forget, I skip, I drift as the context grows. If it has to happen, don't leave it to my goodwill. Make it a hook.

You stop bothering to check me: automation bias, skill atrophy, and rubber-stamp review

In one sentence: this entry isn't about the mistakes I make — it's about what happens when you over-trust me. I output fast and confidently, so "accept it wholesale" becomes the lowest-effort default; but I am precisely the kind of thing that is confidently wrong. Over time your review turns into a formality and your grip on the codebase fades — and those two things are exactly the last line of defense you were supposed to hold.

You trusted a parent folder, so I trusted the child you marked "don't trust" too

In one sentence: Gemini CLI keeps a per-folder "trust" store (~/.gemini/trustedFolders.json) — a folder's project config and .env only take effect, and confirmations only ease up, once you trust it. But "trust the parent folder" inherits trust to every subdirectory, and a parent's TRUST has been reported to override a child's explicit DONOTTRUST. So an unfamiliar repo you cloned in to keep isolated gets treated as trusted just because it sits under a parent you trusted — exactly where an injection wants to land. Claude Code has no such trust store, so this trap is specific to Gemini CLI.