Amir Sharif
Engineer.
Weekend hacker.
Self improvement enthusiast.

Every Tip I Know For Shipping Products That Match The Spec

1. Pin the mock / use AI for pixel perfection

I use Cleanshot to take a screenshot of a mock and Pin that while developing. It’ll create a fixed floating window that stays on top of other windows. It’s often easier than alt-tabbing or if you don’t have/want the screen real estate of keeping your IDE, browser window, and Figma window all open at the same time. Keep working until your code looks 1:1 with the mock.

In some cases, I’ll use AI to call out any discrepencies which is especially true for more complicated screens with lot of text.

Another alternative if you want to catch small differences is Kaleidoscope. A free alternative (for text only) is diffchecker.com

2. Auto review code before sending out a PR

Our team loves tools like Graphite which will review code for you, but you can use a CLI as well.

system: |
  <Diff>
    $input
  </Diff>

  You are an expert FANG engineer reviewing a pull request.

  Suggest any improvements, optimizations, and keep an eye out for any bugs or things that look misplaced.
  Iterate file by file, and then end your message by reviewing the entire commit together and providing commentary.
  For any changes you suggest, please output the modified code or some example code.
  When making suggestions, please reference the specific lines you want to change before your code block.

  Pay particular attention to strings or frontend code that may contain typos, grammar mistakes, or other language problems.

  When prefacing any potential mistake use this emoji to start that line: 
  When reviewing a file that has no issues, preface with: 

  Do not include any introductory message. No yapping.

I run commitmsg before sending out a PR, which diffs and then sends the output to an LLM using the llm tool.

commitmsg='git show --no-color HEAD -- '\'':!sorbet/rbi/dsl'\'' '\'':!sorbet/rbi/gems'\'' '\'':!yarn.lock'\'' '\'':!fixtures'\'' | llm -t commitmsg'

It has caught lots of nits and issues for me before a human ever looks at it!

Often time I add a bunch of debug/testing statements. Sometimes I accidentally send these out in my PR, and it’s only caught by another reviewer.

I’ve created snippets for all my debugging statements in all language, and I also use a special token” called __removeme__

"Pry (Ruby)": {
  "prefix": "pry",
  "body": "binding.pry # __removeme__",
  "scope": "ruby"
},
"Debugger (TS)": {
  "prefix": "pry",
  "body": "debugger // __removeme__",
  "scope": "javascript, typescript"
},
"Remove  Me": {
  "prefix": "rmme",
  "body": "$LINE_COMMENT __removeme__",
},

(I use the same pry snippet in Typescript/Javascript because 1 less thing I have to remember!)

Whenever I make a change or add some code that I want to make sure I don’t commit, I’ll add the __removeme__ to the end of it. Adding it to the end of every debugging statement is just a convenience.

This allows me to setup a git prehook which will prevent me from ever committing something that contains __removeme__

#!/usr/bin/env bun
// This is in .git/hooks/pre-commit

import { $ } from "bun";

try {
  // Get staged files and check for "__removeme__" in a single operation
  const output = await $`git diff --cached -G"__removeme__" --name-only`.text();

  if (output.trim()) {
    const files = output.trim().split('\n');
    console.error('Error: Found "__removeme__" in the following files:');
    console.error(files.join('\n'));
    console.error('\nPlease remove debug statements before committing.');
    process.exit(1);
  }

  process.exit(0);
} catch (error) {
  console.error('Error executing pre-commit hook:', error);
  process.exit(1);
}

3. Setup fast feedback cycles

Use watchexec or another file watcher for fast feedback cycles. For example, let’s say you’re working on a test and you want to re-run it each time you save in your IDE. I use this alias:

wspec='watchexec --stop-timeout 0 -e rb,ts,jbuilder -- bundle exec spring rspec "$@" --fail-fast'

wspec mytest_spec.rb

If I don’t have a spec/test file, I’ll add some content in a scratch.rb file. This file doesn’t get commited, via a personal .gitignore

Let’s say you’re debugging a function that has strange behavior - you don’t want to write tests, because you already have a repro on a REPL/console, but changing the method seems to break other cases.

# scratch.rb
passed = [
  foo(a),
  foo(b),
  foo(c),
  foo(d),
].all? { true }
puts "Passed: #{passed}"

Then I run watchexec --stop-timeout 0 -e rb -- rails runner scratch.rb

Now I can modify foo over and over again and each time I hit save it’ll tell me if I fixed it. This is much faster than having to write a test case, scaffolding all the data, etc.

You can also put requests in here, via curl and mimic what is happening on the network. Repeat the UX, copy the cURL as below and put it in your scratch.rb file.

# scratch.rb
request_1 = `curl ...`
request_2 = `curl ...`
request_3 = `curl ...`

passed = [
  foo(a),
  foo(b),
  foo(c),
  foo(d),
].all? { true }

puts "Passed: #{passed}"

For more complex UI/UX flows, think about creating reproducible scaffolds that you can run in the test or development environment. For example, I was working on a feature that relied on realistic” data across several connected data models, differences in time, and various states. I’ll create a class that’ll help me set that up.

class TestUtils
  def setup_some_scenario
    # ...
  end
end

While implementing this for a test case in the test environment is something you’ll have to do anyway, it’s often better to keep it outside of the test environment so you can run it in development.

Usually these scripts are extracted from my scratch.rb file when it gets really big.



Date
December 31, 2024