A client I'd been talking to for two days sent me a link to their codebase. Clone it, npm install, npm start, then send back a screenshot of the landing page and your thoughts on the architecture. Friendly message. Reasonable-sounding request. We'd get on a call after.
I didn't run it. I want to explain why, because the reasoning generalises far beyond this one repo, and because the request was built so that running it looked like the only way to cooperate.
The screenshot is the whole ask
Read that request again and notice which part can't be faked.
Feedback on the architecture? I can give that by reading the code. Opinion on the landing page design? Also readable: the markup and the styles are right there. Strengths and weaknesses of the project? Reading, again.
The screenshot is the only deliverable in that list that requires executing their code on my machine. Everything else I can produce with my eyes. And the screenshot was framed as the deliverable that mattered — the thing to bring to the call.
Once you see it, you can't unsee it. The request isn't "help us evaluate our work." The request is "please run this."
My machine holds wallet keys, API tokens, and live browser sessions for every account I work with. A postinstall hook or a compromised dev server gets all of it in the time it takes for the install to finish. This is the well-documented fake-recruiter pattern — it has drained a lot of developers who assumed a client repo was a safe repo.
So the rule I work by: read a stranger's repository, never execute it. Not npm install, not npm start, not "just to see how it looks."
Reading is not a downgrade
Here's what surprised me the first time I did this properly: reading found more than running would have.
Running the project would have shown me a landing page. A landing page tells you almost nothing. It's the part that's easiest to buy, copy, or commission for $200. Reading told me what the project actually was.
I pulled the file tree from the host's API and fetched individual files as raw text. No clone, no dependency install, no execution. It took about forty minutes.
The contracts didn't work. This was a DeFi project, so the contracts are where the money lives and where I look first. The staking function transferred the user's deposit to the zero address instead of holding it in the contract. With a standard OpenZeppelin token that call reverts, so staking simply couldn't happen; with a permissive token, every deposit would burn permanently. The reward function took the payout amount as an argument from the caller. Anyone who had ever staked could ask for any number and the contract would pay it. No accounting, no cap. Another function handed out tokens to whoever called it, unlimited.
These aren't subtle bugs you find with a fuzzer at 3am. They're visible on a first read.
The project wasn't what its README said. The README described a decentralised exchange with pooling, liquidity and an AMM. The contracts were a tutorial-grade staking demo with placeholder names. The frontend shipped assets belonging to a different, real product. And under all of it sat a generic e-commerce backend — routes for products, orders and payments, database models for Category, Warehouse and Dispatcher. Nothing in that backend had anything to do with a token or a trade.
The history was one commit. A single "initial commit", dated three weeks earlier. No development history at all. And .env was tracked in the repository. Empty values today, but the moment someone fills them, the credentials live in git history permanently.
None of that required running anything.
The checks, if you want them
This takes ten minutes and costs nothing:
-
package.jsonscripts first.postinstallandpreinstallrun automatically the moment you install. Read whatstartactually launches. In this repo it started a whole server I hadn't looked at yet. - Dependency sources. Anything pointing at a URL, a git address or a local tarball instead of the registry deserves an explanation.
- Commit history. One commit with no history means the code has no provenance. It doesn't prove bad intent, plenty of legitimate teams squash an import, but it means you're being handed something whose origin you can't check.
- README against reality. Does the code do what the description claims? A large gap is worth understanding before you sign anything.
-
Files that don't belong. Committed
.envfiles, credentials, one file far larger than its neighbours, obfuscated blobs.
Read files as raw text through the host's own API. GitHub, GitLab and Bitbucket all serve raw file contents and directory listings over HTTP. Text can't execute.
What to say instead of yes
Refusing doesn't have to cost you the client. What I offer:
- Send a deployed URL. If you want my opinion on the landing page, point me at it running on your infrastructure. I'll judge it as a user does. A real team has a staging environment, or can stand one up in an afternoon.
- Or show me on a call. Share your screen. I'll react live, which is faster than a screenshot anyway.
- Or pay for a short review. A few hours at my rate, with a written report you keep either way.
Every one of those gets a genuine client exactly what they said they wanted. None of them puts their code on my machine.
And that's the part worth internalising: the refusal is a filter, not a loss. A real client doesn't care which of those three you pick. They wanted the feedback, and they're getting it. The only person who insists on the local run is the one for whom the run was the point.
The other half of the same request
There's a second thing going on here, and it's quieter.
An architecture review, a security read of a contract set, an assessment of what to fix and in what order: that is consulting work, and it's the thing clients pay for. "Just take a quick look and tell us what you think" is a request for that work, unpriced, dressed up as a formality before the real engagement starts.
I did write up my findings for this client, in detail and for free. One time, and for one reason: I was reading the code anyway to decide whether it was safe, so the security check and the review were the same hour of work. I sent the findings, said plainly what I'd charge to go further, and stopped there.
That's the boundary I'd suggest to anyone doing this work. One free read, if it happens to coincide with your own due diligence. Then a number.
What actually happened
I sent the client the findings — the zero-address transfer, the caller-supplied reward amount, the mismatch between the README and the code, the committed .env. I explained why I don't run unfamiliar code, asked for a deployed URL instead of a screenshot, and asked two direct questions: what in this repository is meant to be yours, and are we fixing this or building the real thing?
I don't know yet how it ends. Either the client turns out to be real, in which case I'm the only candidate who opened the code and told them the truth about it. That is a much better position than the one where I sent a screenshot. Or they disappear, which answers the question just as well.
Either way I didn't run it.
Top comments (0)