[{"content":"If you\u0026amp;rsquo;re new to this blog, these are the posts I\u0026amp;rsquo;d start with. They capture the ideas I return to most often when thinking about writing software and working effectively as an engineer.\nDelivering Work How to Deliver Code Every Day\nA practical system for consistently delivering work.\nOn commenting and approving pull requests\nA simple default for adding value to colleagues\u0026amp;rsquo; work while keeping it moving.\nThings I Check Before Opening a PR\nMy checklist for making pull requests easy to review and safe to merge.\nWhy Great PRs Are Great: Pull Requests Templates\nHow a simple …","permalink":"/start-here/","summary":"If you\u0026rsquo;re new to this blog, these are the posts I\u0026rsquo;d start with. They capture the ideas I return to most often when thinking about writing software and working effectively as an engineer.\n","title":"Start Here"},{"content":" Hi there! I\u0026amp;rsquo;m Jake.\nMy mission is to empower and propel growing businesses with thoughtful, high-quality software solutions.\nI\u0026amp;rsquo;m a senior software engineer and engineering leader with over a decade of experience building and shipping software in small, high-trust teams. I learn in public, write every day, and focus on making both systems and people better through continuous improvement.\nAs a leader, I cut through ambiguity by asking curious, pointed questions that move projects directly toward shipping. I\u0026amp;rsquo;m known for making hard problems feel easier by exposing scope and …","permalink":"/about/","summary":" Hi there! I\u0026rsquo;m Jake.\nMy mission is to empower and propel growing businesses with thoughtful, high-quality software solutions.\nI\u0026rsquo;m a senior software engineer and engineering leader with …","title":"About"},{"content":"After more than a decade in the field as an engineering leader and hands-on practitioner, I\u0026amp;rsquo;m thrilled to be offering a consulting service.\nI am a strong contributor in three areas: technical consulting, mentorship, and workshops \u0026amp;amp; teaching.\nTechnical consulting I love digging into tough problems in greenfield and legacy code. A few things I\u0026amp;rsquo;m good at:\nRaising code quality (tooling, testing, monitoring \u0026amp;amp; observability, building CI/CD pipelines) Big scary bugs Process improvement Internal/external communication and meeting/ceremony refinement Refactoring Complex feature …","permalink":"/consulting/","summary":"After more than a decade in the field as an engineering leader and hands-on practitioner, I\u0026rsquo;m thrilled to be offering a consulting service.\nI am a strong contributor in three areas: technical …","title":"Consulting"},{"content":"Projects One of my goals is to launch at least one new project every year. I\u0026amp;rsquo;m making small bets and staying comfortable with failure.\n2026 BirdWalk (building!): \u0026amp;ldquo;Let\u0026amp;rsquo;s go on a birdwalk!\u0026amp;rdquo; A bird identification game based on real birding data.\nTypeNotes: A daily learning site for professional JavaScript and TypeScript developers, particularly intermediate-to-senior engineers working with TypeScript in production.\nYes, It\u0026amp;rsquo;s a Problem: An OpenAI-powered hobby application that confirms all your fears.\n2025 Don\u0026amp;rsquo;t Stay Stuck: A Debugging Guide for Rising …","permalink":"/projects-and-talks/","summary":"Projects One of my goals is to launch at least one new project every year. I\u0026rsquo;m making small bets and staying comfortable with failure.\n2026 BirdWalk (building!): \u0026ldquo;Let\u0026rsquo;s go on a …","title":"Projects \u0026 Talks"},{"content":"For each way you can map something in Vim, you can inspect those mappings. Check out this table of commands:\nCOMMANDS MODES :map :noremap :unmap Normal, Visual, Select, Operator-pending :nmap :nnoremap :nunmap Normal :vmap :vnoremap :vunmap Visual and Select :smap :snoremap :sunmap Select :xmap :xnoremap :xunmap Visual :omap :onoremap :ounmap Operator-pending :map! :noremap! :unmap! Insert and Command-line :imap :inoremap :iunmap Insert :lmap :lnoremap :lunmap Insert, Command-line, Lang-Arg :cmap :cnoremap :cunmap Command-line :tmap :tnoremap :tunmap Terminal-Job To take a very practical one, …","permalink":"/tils/vim-view-all-your-maps/","summary":"For each way you can map something in Vim, you can inspect those mappings. Check out this table of commands:\nCOMMANDS MODES :map :noremap :unmap Normal, Visual, Select, Operator-pending :nmap …","title":"Vim view all your maps"},{"content":"We can save all evaluated commands from our TSX REPL session to a file:\n$ npx tsx \u0026amp;gt; const name: number = \u0026amp;#34;Jake\u0026amp;#34; undefined \u0026amp;gt; .save output.ts Session saved to: output.ts One use case would be evaluating that output with tsc to get compilation and type-checking:\n$ npx tsc output.ts output.ts:1:7 - error TS2322: Type \u0026amp;#39;string\u0026amp;#39; is not assignable to type \u0026amp;#39;number\u0026amp;#39;. 1 const name: number = \u0026amp;#34;Jake\u0026amp;#34; ~~~~~~~~ Found 1 error in output.ts:1 See .help inside TSX for more info about this and other meta commands.\ntsx - NPM ","permalink":"/tils/saving-tsx-evaluated-commands-to-file/","summary":"We can save all evaluated commands from our TSX REPL session to a file:\n$ npx tsx \u0026gt; const name: number = \u0026#34;Jake\u0026#34; undefined \u0026gt; .save output.ts Session saved to: output.ts ","title":"Saving TSX's Evaluated Commands to File"},{"content":"Want to get better at technical writing? I\u0026amp;rsquo;ve been writing prose (READMEs, wikis, and this blog) with Vale for months. It\u0026amp;rsquo;s helps me write better, faster. Here\u0026amp;rsquo;s what I\u0026amp;rsquo;ve learned.\nVale is a \u0026amp;ldquo;linter for prose.\u0026amp;rdquo; It gives you tips like this.\n$ vale README.md README.md 4:3 error Did you really mean \u0026amp;#39;existant\u0026amp;#39;? Vale.Spelling 6:12 warning Use \u0026amp;#39;use\u0026amp;#39; instead of \u0026amp;#39;utilize\u0026amp;#39;. Microsoft.Vocab 9:1 suggestion In general, use active voice. write-good.Passive ✖ 1 error, 1 warning and 1 suggestion in 1 file. It\u0026amp;rsquo;s running in my text editor with …","permalink":"/tils/writing-prose-with-vale/","summary":"Want to get better at technical writing? I\u0026rsquo;ve been writing prose (READMEs, wikis, and this blog) with Vale for months. It\u0026rsquo;s helps me write better, faster. Here\u0026rsquo;s what I\u0026rsquo;ve …","title":"Writing Prose with Vale"},{"content":"In the next twelve months, I\u0026amp;rsquo;m launching twelve software startups.\nThis post is a tool to document my intention and hold myself accountable.\nWhat this means For years, I\u0026amp;rsquo;ve had an itch: I\u0026amp;rsquo;m not building software projects for me. Projects that scratch my itches, show what I\u0026amp;rsquo;m capable of, and generate my own revenue. I\u0026amp;rsquo;d like that to change. The path forward is to force myself to start shipping products.\nSomeone wise once said: \u0026amp;ldquo;If you don\u0026amp;rsquo;t make things, then what you have to offer is your taste.\u0026amp;rdquo; I\u0026amp;rsquo;d like to start demonstrating a bit more …","permalink":"/posts/12-startups-in-12-months/","summary":"In the next twelve months, I\u0026rsquo;m launching twelve software startups.\n","title":"12 Startups in 12 Months"},{"content":"Today I discovered tsx, a REPL that evaluates TypeScript. You can run it with npx:\nnpx tsx And it evaluates TypeScript.\n\u0026amp;gt; const onClick = (input: number):void =\u0026amp;gt; input + 1 undefined \u0026amp;gt; onClick(1) 2 It does not check types, so you can run this just fine:\n\u0026amp;gt; const greeting: number = \u0026amp;#34;Hello world!\u0026amp;#34; // Number, or string? undefined For that, you\u0026amp;rsquo;ll need a compiler like tsc. tsx can transpile TypeScript, but it does not type-check it.\ntsx npm ","permalink":"/tils/tsx-a-repl-for-typescript/","summary":"Today I discovered tsx, a REPL that evaluates TypeScript. You can run it with npx:\nnpx tsx And it evaluates TypeScript.\n\u0026gt; const onClick = (input: number):void =\u0026gt; input + 1 undefined \u0026gt; …","title":"tsx, a REPL for TypeScript"},{"content":"For the golfers out there, we can combine these two lines into one:\nconst items = await getCollection(); return items.sort(); This doesn\u0026amp;rsquo;t work because sort() is being called on the promise returned by getCollection():\nreturn await getCollection().sort(); // `sort()` runs on the Promise Instead, wrap the await expression in parentheses so sort() is called on the resolved value:\nreturn (await getCollection()).sort(); // `sort()` runs on the resolved array Promise documentation - MDN ","permalink":"/tils/enumerate-on-promised-resolved/","summary":"For the golfers out there, we can combine these two lines into one:\nconst items = await getCollection(); return items.sort(); This doesn\u0026rsquo;t work because sort() is being called on the promise …","title":"Enumerate on promise resolved"},{"content":"Today I\u0026amp;rsquo;d like to highlight something I built with some colleagues that I think is cool: the dynamic paths of Hashrocket\u0026amp;rsquo;s daily learning site, Today I Learned (TIL).\nCheck out the browser bar here:\nThe URL it contains is:\nhttps://til.hashrocket.com/posts/ee0a1bc878-parameters-filtering\nWhat do you notice? Here\u0026amp;rsquo;s my list:\nAfter posts/, the path starts with ee0a1bc878 (?) That\u0026amp;rsquo;s followed by a dash, then the post title in kebab-case (parameters-filtering) When you visit the same URL with an altered kebab-case title (e.g. ee0a1bc878-nope), you get the same page 😵‍💫 When an …","permalink":"/posts/til-urls/","summary":"Today I\u0026rsquo;d like to highlight something I built with some colleagues that I think is cool: the dynamic paths of Hashrocket\u0026rsquo;s daily learning site, Today I Learned (TIL).\n","title":"Opaque IDs, editable titles in TIL URLs"},{"content":"Astro\u0026amp;rsquo;s default port is 4321. Like Vite, this project is trying to convey its (rocket-launch) branding, even in this small detail.\nThe proposal for this change includes an interesting argument. By choosing a non-3000 port, the developer\u0026amp;rsquo;s browser is unlikely to already have a favicon cached. This gives the Astro favicon and branding a solid chance of being seen every time by the developer.\nAstro 4321 proposal\n","permalink":"/tils/4321-is-astro/","summary":"Astro\u0026rsquo;s default port is 4321. Like Vite, this project is trying to convey its (rocket-launch) branding, even in this small detail.\n","title":"4321 is Astro"},{"content":"Node Timeout timers have a private boolean attribute called _destroyed. It tells you if the function has been cleared:\ntimer._destroyed; One way to make it true it is to let the function be called:\n\u0026amp;gt; const timer = setTimeout(() =\u0026amp;gt; { | console.log(\u0026amp;#34;Done!\u0026amp;#34;); | }, 5000); \u0026amp;gt; timer._destroyed false // Alive \u0026amp;gt; Done! // Function is called \u0026amp;gt; timer._destroyed // Destroyed 💥 true Another is to call clearTimeout:\n\u0026amp;gt; const timer = setTimeout(() =\u0026amp;gt; { | console.log(\u0026amp;#34;We\u0026amp;#39;ll destroy before this is called!\u0026amp;#34;); | }, 5000); \u0026amp;gt; clearTimeout(timer) // Destroying the timer …","permalink":"/tils/nodes-destroyed-private-method/","summary":"Node Timeout timers have a private boolean attribute called _destroyed. It tells you if the function has been cleared:\ntimer._destroyed; ","title":"Node's Timeout _destroyed Private Method"},{"content":"Today I used JavaScript to mass-unsubscribe from a website\u0026amp;rsquo;s email notifications. Here\u0026amp;rsquo;s the code, followed by what I did and why.\ndocument .querySelectorAll(\u0026amp;#39;[role=\u0026amp;#34;switch\u0026amp;#34;]\u0026amp;#39;) .forEach((element) =\u0026amp;gt; element.click()); Today I was interacting with a website that specializes in job postings. To unsubscribe to their emails, you must visit a notifications page and un-check a toggle to opt out of each kind of email you\u0026amp;rsquo;d like to not receive.\nPerusing the DOM taught me that each button has a role of \u0026amp;ldquo;switch.\u0026amp;rdquo; This JavaScript unsubscribed me from every …","permalink":"/tils/uncheck-buttons-with-javascript/","summary":"Today I used JavaScript to mass-unsubscribe from a website\u0026rsquo;s email notifications. Here\u0026rsquo;s the code, followed by what I did and why.\ndocument …","title":"Uncheck Buttons With JavaScript"},{"content":"Reflection is an important tool in programming; it lets us examine a language\u0026amp;rsquo;s structure. Can we reflect in JavaScript? Sort of.\nSome functions can be reflected with toString():\n\u0026amp;gt; const one = () =\u0026amp;gt; 1 undefined \u0026amp;gt; one.toString() \u0026amp;#39;() =\u0026amp;gt; 1\u0026amp;#39; Very cool! But native functions only return this:\n\u0026amp;gt; Math.max.toString() \u0026amp;#39;function max() { [native code] }\u0026amp;#39; You must read the ECMA Script specification to know what the function is required to do. The actual implementation is written in the JavaScript engine, likely in another language like C++, which is why Node can\u0026amp;rsquo;t …","permalink":"/tils/js-reflection-with-to-string/","summary":"Reflection is an important tool in programming; it lets us examine a language\u0026rsquo;s structure. Can we reflect in JavaScript? Sort of.\nSome functions can be reflected with toString():\n\u0026gt; const one …","title":"JavaScript Reflection with toString"},{"content":"A vital testing skill is focusing a test. When iterating on a failing test, we want that test under microscope.\nThe way I do this in JavaScript is .only:\nimport {describe, expect, test} from \u0026amp;#39;vitest\u0026amp;#39;; import {sum} from \u0026amp;#39;./sum.js\u0026amp;#39;; test(\u0026amp;#39;adds 1 + 2 to equal 3\u0026amp;#39;, () =\u0026amp;gt; { expect(sum(1, 2)).toBe(3); }); describe.only(\u0026amp;#39;adding fours\u0026amp;#39;, () =\u0026amp;gt; { test(\u0026amp;#39;adds 4 + 4 to equal 8\u0026amp;#39;, () =\u0026amp;gt; { expect(sum(4, 4)).toBe(8); }); }); When this test runs, only the describe will run, the other is skipped.\nThis works on test, it, and describe, in Jest, Vitest, and probably …","permalink":"/tils/focus-a-js-test/","summary":"A vital testing skill is focusing a test. When iterating on a failing test, we want that test under microscope.\nThe way I do this in JavaScript is .only:\nimport {describe, expect, test} from …","title":"Focus a JS Test"},{"content":"JSDoc has a @deprecated tag that you can use in TypeScript, too. Here\u0026amp;rsquo;s an example.\n// App.tsx interface User { fullName: string; /** @deprecated use fullName field instead */ lastName: string; } function App() { const user: User = {fullName: \u0026amp;#39;jake worth\u0026amp;#39;, lastName: \u0026amp;#39;worth\u0026amp;#39;}; return \u0026amp;lt;\u0026amp;gt;{user.lastName}\u0026amp;lt;/\u0026amp;gt;; } export default App; In my Vim text editor, COC\u0026amp;rsquo;s TypeScript server prints this when hover on the use of user.lastName:\n\u0026amp;#34;lastName\u0026amp;#34; is deprecated. Related information: * App.tsx#6,7: The declaration was marked as deprecated here. (tsserver 6385) …","permalink":"/tils/js-doc-deprecations-in-typescript/","summary":"JSDoc has a @deprecated tag that you can use in TypeScript, too. Here\u0026rsquo;s an example.\n","title":"JSDoc Deprecations in TypeScript"},{"content":"I\u0026amp;rsquo;m reading How to Open Source by Richard Schneeman (fantastic) and one of his tips is to show documentation code like this (consider documenting ls):\nls \u0026amp;lt;desired-path\u0026amp;gt; Rather than the common:\nls /your/path/here The idea is that with the latter, someone will copy your/path/here into their terminal, producing an error like this:\nls: /your/path/here: No such file or directory This error says: \u0026amp;ldquo;The directory doesn\u0026amp;rsquo;t exist.\u0026amp;rdquo; Which could beget the incorrect question: \u0026amp;ldquo;How do I create the /your/path/here directory?\u0026amp;rdquo;\nDoing the same with brackets produces this …","permalink":"/tils/unknown-value-in-brackets/","summary":"I\u0026rsquo;m reading How to Open Source by Richard Schneeman (fantastic) and one of his tips is to show documentation code like this (consider documenting ls):\nls \u0026lt;desired-path\u0026gt; Rather than the …","title":"Unknown Value in Brackets"},{"content":"When upgrading a dependency for a security issue, we might upgrade and move on:\nnpm install \u0026amp;lt;dependency\u0026amp;gt;@\u0026amp;lt;version\u0026amp;gt; But wait! Was every \u0026amp;lt;dependency\u0026amp;gt; version upgraded, or just the version in package.json? Check with:\nnpm list // or npm ls Packages like Prettier might only be in your lockfile once:\nnpm list prettier hugo@ /Users/dev/code/hugo └── prettier@3.8.3 -\u0026amp;gt; ./node_modules/.pnpm/prettier@3.8.3/node_modules/prettier But packages like lodash are used by many dependency libraries, even in production:\nnpm list lodash fe@0.1.0 /dev/frontend ├─┬ …","permalink":"/tils/npm-list-packages/","summary":"When upgrading a dependency for a security issue, we might upgrade and move on:\nnpm install \u0026lt;dependency\u0026gt;@\u0026lt;version\u0026gt; But wait! Was every \u0026lt;dependency\u0026gt; version upgraded, or just the …","title":"NPM List Packages"},{"content":"Today while reading the Rust Documentation, I encountered a fantastic UX touch. The authors provide this image with each code block that intentionally doesn\u0026amp;rsquo;t compile:\nDocs have to show code that\u0026amp;rsquo;s broken; it\u0026amp;rsquo;s how you learn. But broken code can also confuse inexperienced programmers. I\u0026amp;rsquo;ve seen broken code from a tutorial pasted into Stack Overflow with a plea for help, when the solution is in the next paragraph of the tutorial.\nEven if the tutorial is correct, readers are getting lost. Why not say: \u0026amp;ldquo;This is broken, but don\u0026amp;rsquo;t worry, we\u0026amp;rsquo;re gonna fix …","permalink":"/tils/rust-erroring-code-visual-aid/","summary":"Today while reading the Rust Documentation, I encountered a fantastic UX touch. The authors provide this image with each code block that intentionally doesn\u0026rsquo;t compile:\n","title":"Rust Erroring Code Visual Aid"},{"content":"Images in Hugo can be a bit tricky. Here\u0026amp;rsquo;s how I\u0026amp;rsquo;ve added images on my projects page that are resized, linked, and have alts: the figure shortcode.\n{{\u0026amp;lt; figure src=\u0026amp;#34;/images/yiap.png\u0026amp;#34; alt=\u0026amp;#34;Yes, It\u0026amp;#39;s a Problem\u0026amp;#34; link=\u0026amp;#34;https://github.com/jwworth/yes-its-a-problem\u0026amp;#34; width=\u0026amp;#34;600\u0026amp;#34; \u0026amp;gt;}} ","permalink":"/tils/resized-images-in-hugo-markdown/","summary":"Images in Hugo can be a bit tricky. Here\u0026rsquo;s how I\u0026rsquo;ve added images on my projects page that are resized, linked, and have alts: the figure shortcode.\n{{\u0026lt; figure …","title":"Resized Images in Hugo Markdown"},{"content":"Sometimes I want to iterate on an alias. You can unalias an alias like this:\nunalias aliasname This is particularly useful when changing an alias to a function. When you change this:\nalias up=\u0026amp;#34;cd ..\u0026amp;#34; To this:\nup() { # Alternative implementation } And source your config, you\u0026amp;rsquo;ll likely see this (Zsh example):\n.zshrc: defining function based on alias `up\u0026amp;#39; .zshrc: parse error near `()\u0026amp;#39; Unaliasing is an elegant path forward.\n","permalink":"/tils/unalias-an-alias/","summary":"Sometimes I want to iterate on an alias. You can unalias an alias like this:\nunalias aliasname ","title":"Unalias an Alias"},{"content":"Today I discovered these two Vim flags:\nvim -w log.txt file vim -s log.txt file The -w flag sends all keystrokes in your session to log.txt. The -s loads Vim and replays all the keystroke back onto that file.\nSo I open file, type lll and :wq, and then my log.txt should have some representation of that saved. When I open file again with log.txt as my script, those same keystrokes will replay after the file opens. The arguments are scriptout and scriptin, respectively.\nThis seems like a massive discovery and I\u0026amp;rsquo;m not sure what the practical applications could be. For now, I\u0026amp;rsquo;m saving …","permalink":"/tils/vim-scriptout-and-scriptin/","summary":"Today I discovered these two Vim flags:\nvim -w log.txt file vim -s log.txt file The -w flag sends all keystrokes in your session to log.txt. The -s loads Vim and replays all the keystroke back onto …","title":"Vim Scriptout and Scriptin"},{"content":"I\u0026amp;rsquo;ve been writing some non-printing characters to a file. But when I concatenate, it appears empty.\ncat log.txt # Nothing here 🥺 cat those non-printing characters with -v:\ncat -v log.txt jjjj:wq^M% See man cat for these details:\n-v Display non-printing characters so they are visible. Control characters print as ‘^X’ for control-X; the delete character (octal 0177) prints as ‘^?’. Non-ASCII characters (with the high bit set) are printed as ‘M-’ (for meta) followed by the character for the low 7 bits. ","permalink":"/tils/cat-non-printing-characters/","summary":"I\u0026rsquo;ve been writing some non-printing characters to a file. But when I concatenate, it appears empty.\ncat log.txt # Nothing here 🥺 cat those non-printing characters with -v:\ncat -v log.txt …","title":"Concatenate Non-Printing Characters"},{"content":"I was doing a Vite demo last week, I changed some markup, and visited the local web server. The markup changed, but the state persisted. What?!\nThis is Vite\u0026amp;rsquo;s Hot Module Replacement API.\nThe Vite React application server logged this when I saved my file:\n9:32:15 AM [vite] (client) hmr update /src/App.tsx \u0026amp;ldquo;hmr\u0026amp;rdquo; is Hot Module Replacement. With it, we get \u0026amp;ldquo;precise updates without reloading the page or blowing away application state.\u0026amp;rdquo; So cool!\nHMR API docs\n","permalink":"/tils/vite-hot-module-replacement/","summary":"I was doing a Vite demo last week, I changed some markup, and visited the local web server. The markup changed, but the state persisted. What?!\nThis is Vite\u0026rsquo;s Hot Module Replacement API.\n","title":"Vite Hot Module Replacement"},{"content":"Prettier formats code in fenced code blocks! When I type three backticks and then a code, Prettier evaluates the code in the block and tries to format it per my global .prettierrc.\nI produce a lot of technical writing in markdown. I\u0026amp;rsquo;ve often wondered if my fenced code blocks are valid, since I sometimes type them by hand. With Prettier, as long as I add a language identifier (e.g. typescript), that language is supported and configured for Prettier autoformatting, it will run on the block.\nYou can disable it with a \u0026amp;lt;!-- prettier-ignore --\u0026amp;gt; comment.\nPrettier reformats the content of …","permalink":"/tils/prettier-in-fenced-code-blocks/","summary":"Prettier formats code in fenced code blocks! When I type three backticks and then a code, Prettier evaluates the code in the block and tries to format it per my global .prettierrc.\n","title":"Prettier in Fenced Code Blocks"},{"content":"If you\u0026amp;rsquo;ve been writing TypeScript for a while, you might have written code like this:\n// utils.ts export type Color = \u0026amp;#39;red\u0026amp;#39; | \u0026amp;#39;blue\u0026amp;#39; | \u0026amp;#39;green\u0026amp;#39;; // app.tsx import {Color} from \u0026amp;#39;./utils\u0026amp;#39;; As of TypeScript 3.8, there\u0026amp;rsquo;s a better way to import!\n// app.tsx import type {Color} from \u0026amp;#39;./utils\u0026amp;#39;; The difference here is one keyword, type. This tells the compiler: \u0026amp;ldquo;Color is a type. You can erase it after transpilation.\u0026amp;rdquo; The export is also erased when we use type.\nThis solves a problem many developers might never have had. Consider this import: …","permalink":"/tils/typescript-type-only-imports-and-exports/","summary":"If you\u0026rsquo;ve been writing TypeScript for a while, you might have written code like this:\n// utils.ts export type Color = \u0026#39;red\u0026#39; | \u0026#39;blue\u0026#39; | \u0026#39;green\u0026#39;; // app.tsx import {Color} …","title":"TypeScript Type-Only Imports and Exports"},{"content":"New Vite TypeScript/React apps create themselves like this:\ncreateRoot(document.getElementById(\u0026amp;#39;root\u0026amp;#39;)!).render(\u0026amp;lt;App /\u0026amp;gt;); What\u0026amp;rsquo;s going on with that !?\nThe ! is TypeScript is the non-null assertion operator. It\u0026amp;rsquo;s a way of saying to our TypeScript compiler: \u0026amp;ldquo;I know that this is not null; don\u0026amp;rsquo;t check it.\u0026amp;rdquo;\nWithout it, you\u0026amp;rsquo;ll see this on apps that have been converted to TypeScript, checking for root before loading the app.\nconst root = document.getElementById(\u0026amp;#39;root\u0026amp;#39;); if (!root) { throw new Error(\u0026amp;#39;Root element not found\u0026amp;#39;); } …","permalink":"/tils/non-null-with-root-import/","summary":"New Vite TypeScript/React apps create themselves like this:\ncreateRoot(document.getElementById(\u0026#39;root\u0026#39;)!).render(\u0026lt;App /\u0026gt;); What\u0026rsquo;s going on with that !?\n","title":"Non-Null With Root Import"},{"content":"I often reflect on a conversation from years ago with a former Army boss. It\u0026amp;rsquo;s foundational to how I understand leadership. He said that being a leader means you have to \u0026amp;ldquo;Genuinely care.\u0026amp;rdquo;\nThis phrase helped me understand all the leaders I\u0026amp;rsquo;d encountered up to that point. Followers will forgive many shortcomings and failures in their leader, if they believe that they truly care about what happens to them.\nIf you\u0026amp;rsquo;ve encountered leaders like this, you know. They might not have made every right decision. But they\u0026amp;rsquo;d put everything on the line before they let …","permalink":"/posts/genuinely-care/","summary":"I often reflect on a conversation from years ago with a former Army boss. It\u0026rsquo;s foundational to how I understand leadership. He said that being a leader means you have to \u0026ldquo;Genuinely …","title":"Genuinely Care"},{"content":"Vite\u0026amp;rsquo;s default port, 5173, is leet code for the project\u0026amp;rsquo;s name:\n5 = \u0026amp;ldquo;V\u0026amp;rdquo; (Roman numerals 😉) 1 = \u0026amp;ldquo;I\u0026amp;rdquo; 7 = \u0026amp;ldquo;T\u0026amp;rdquo; 3 = \u0026amp;ldquo;E\u0026amp;rdquo; It also happens to be a different number from any project I\u0026amp;rsquo;ve worked on, so that\u0026amp;rsquo;s a DX win, too.\nVite pull request\n","permalink":"/tils/5173-is-vite/","summary":"Vite\u0026rsquo;s default port, 5173, is leet code for the project\u0026rsquo;s name:\n5 = \u0026ldquo;V\u0026rdquo; (Roman numerals 😉) 1 = \u0026ldquo;I\u0026rdquo; 7 = \u0026ldquo;T\u0026rdquo; 3 = \u0026ldquo;E\u0026rdquo; ","title":"5173 is Vite"},{"content":"To load Cursor with context of the current directory, use:\ncursor . This opens the program with the current directory loaded up, ready for agent planning or execution.\nThis requires the Cursor command-line module, which you\u0026amp;rsquo;ll likely install when installing Cursor.\n","permalink":"/tils/cursor-on-current-directory/","summary":"To load Cursor with context of the current directory, use:\ncursor . ","title":"Cursor on Current Directory"},{"content":"Today I learned about the Outbox Pattern.\nThe Outbox Pattern solves a common problem. Consider a system that charges a payment, processes an order, and then sends an email.\nThe payment, order-processing, and email-sending all happen in a database transaction— if any of them fails, we roll back. A question: when the payment and order succeed, do we wait for the email to send before deciding that the transaction succeeded? What it the email-sending fails; do we roll back the payment and order?\nA solution is the Outbox Pattern. Instead of sending the email in the transaction, we record the email …","permalink":"/tils/the-outbox-pattern/","summary":"Today I learned about the Outbox Pattern.\n","title":"The Outbox Pattern"},{"content":"Always leave something behind. If you find something interesting, leave a trace that you were there.\nThis is a little thing that over time leads to a friendlier, more humane internet. Here are a few examples.\nBlog post that helped you Imagine you\u0026amp;rsquo;re reading a blog post and you find it helpful. Or, you agree with it, but find one part of the argument lacking. Before moving on, leave a comment!\nAs a writer, receiving feedback on my work is welcome and rare. This blog gets thousands of readers a month, and yet the amount of direct feedback I\u0026amp;rsquo;ve received over all the years is a small …","permalink":"/posts/leave-a-trace/","summary":"Always leave something behind. If you find something interesting, leave a trace that you were there.\n","title":"Leave a Trace"},{"content":"After version v12.17.0, Node\u0026amp;rsquo;s REPL provides a useful preview effect. In this example, 2 is shown in gray before I hit enter.\n\u0026amp;gt; 1 + 1 2 But what if I don\u0026amp;rsquo;t want that? Here\u0026amp;rsquo;s a file called repl that disables it as an option:\n#!/usr/bin/env node const repl = require(\u0026amp;#39;node:repl\u0026amp;#39;); repl.start({preview: false}); Then:\n$ ./repl \u0026amp;gt; 1 + 1 No preview.\nDocs\n","permalink":"/tils/node-repl-no-preview/","summary":"After version v12.17.0, Node\u0026rsquo;s REPL provides a useful preview effect. In this example, 2 is shown in gray before I hit enter.\n\u0026gt; 1 + 1 2 But what if I don\u0026rsquo;t want that? Here\u0026rsquo;s a …","title":"Node REPL, No Preview"},{"content":"With my Node versions managed via NVM, I want to be on the latest LTS. You can ensure that happens with these commands:\n$ nvm install --lts $ nvm alias default lts/* default -\u0026amp;gt; node (-\u0026amp;gt; v24.16.0) These two commands combine to say: \u0026amp;ldquo;Install the latest LTS, and make my default that version.\u0026amp;rdquo;\nSee nvm --help for more information.\n","permalink":"/tils/nvm-install-and-use-latest/","summary":"With my Node versions managed via NVM, I want to be on the latest LTS. You can ensure that happens with these commands:\n$ nvm install --lts $ nvm alias default lts/* default -\u0026gt; node (-\u0026gt; …","title":"NVM Install and Use Latest LTS"},{"content":"How do I \u0026amp;ldquo;get up to speed\u0026amp;rdquo; in a new codebase? Changing it.\nAfter trying almost every alternative— reading code, docs, tickets, metrics— I\u0026amp;rsquo;ve found nothing that teaches a system faster than shipping a small change.\nNote: I wrote something similar to this a few years ago, which you can read here: Stop Studying, Start Building. That advice was for people learning to code; this is for starting on a new project, which is something you\u0026amp;rsquo;ll do again and again a programmer. It\u0026amp;rsquo;s worth trying to do well.\nReading the Code The first thing I\u0026amp;rsquo;ve tried is reading the code. …","permalink":"/posts/you-learn-a-codebase-by-changing-it/","summary":"How do I \u0026ldquo;get up to speed\u0026rdquo; in a new codebase? Changing it.\n","title":"You Learn a Codebase by Changing It"},{"content":"Approach caching with caution.\nIn this post, I\u0026amp;rsquo;d like to describe some important considerations that I think teams should consider before applying a caching solution. I\u0026amp;rsquo;ve found:\nCaching is often applied too early. Caching can be a slippery slope. Caching is hard to do right. Often Applied Too Early Caching is often applied too early. It\u0026amp;rsquo;s wise to try other alternatives first.\nHere\u0026amp;rsquo;s an example: suppose we have an order-management system that fetches all the orders, and it\u0026amp;rsquo;s performing slowly. Should we cache it?\nWe could. But first, here are some good questions. …","permalink":"/posts/avoid-hasty-caching/","summary":"Approach caching with caution.\n","title":"Avoid Hasty Caching"},{"content":"The Law of Demeter is important in object-oriented programming. But where does \u0026amp;ldquo;Demeter\u0026amp;rdquo; come from?\nAKA the \u0026amp;ldquo;Principle of Least Knowledge,\u0026amp;rdquo; here\u0026amp;rsquo;s a simple example of the law: code that lets us ask an owner, \u0026amp;ldquo;What is your dog\u0026amp;rsquo;s breed?\u0026amp;rdquo;\nclass Dog belongs_to :owner belongs_to :breed def breed_name breed.name end end class Owner has_one :dog # ✅ Owner only knows about \u0026amp;#39;breed_name\u0026amp;#39;, # not what \u0026amp;#39;breed\u0026amp;#39; and \u0026amp;#39;name\u0026amp;#39; are. def dog_breed_name dog.breed_name end # ❌ Owner knows that dog has a \u0026amp;#39;breed\u0026amp;#39;, and # that …","permalink":"/tils/law-of-demeter-origin-story/","summary":"The Law of Demeter is important in object-oriented programming. But where does \u0026ldquo;Demeter\u0026rdquo; come from?\n","title":"Law of Demeter Origin Story"},{"content":"Today I learned how to fill in an emoji with CSS. Here\u0026amp;rsquo;s an example:\n.filledEmoji { color: transparent; text-shadow: \u0026amp;#39;0 0 0 #86efac\u0026amp;#39;; } This gave the microbe emoji (🦠) a filled style in my Conway\u0026amp;rsquo;s Game of Life project.\n","permalink":"/tils/filled-emoji-with-css/","summary":"Today I learned how to fill in an emoji with CSS. Here\u0026rsquo;s an example:\n.filledEmoji { color: transparent; text-shadow: \u0026#39;0 0 0 #86efac\u0026#39;; } ","title":"Filled Emoji With CSS"},{"content":"What I call \u0026amp;ldquo;wraparound\u0026amp;rdquo; are repeatable indices on an array. The common use case is a carousel. Here\u0026amp;rsquo;s how it\u0026amp;rsquo;s done:\nconst items = [\u0026amp;#39;first\u0026amp;#39;, \u0026amp;#39;middle\u0026amp;#39;, \u0026amp;#39;last\u0026amp;#39;]; const i = 0; // Our iterable index // \u0026amp;#34;Next\u0026amp;#34; -\u0026amp;gt; const nextIndex = (i + 1) % items.length; // \u0026amp;lt;- \u0026amp;#34;Previous\u0026amp;#34; const prevIndex = (i - 1 + items.length) % items.length; ","permalink":"/tils/javascript-wraparound-index/","summary":"What I call \u0026ldquo;wraparound\u0026rdquo; are repeatable indices on an array. The common use case is a carousel. Here\u0026rsquo;s how it\u0026rsquo;s done:\nconst items = [\u0026#39;first\u0026#39;, \u0026#39;middle\u0026#39;, …","title":"JavaScript Wraparound Index"},{"content":"Here\u0026amp;rsquo;s a periodic reminder that JavaScript supports string concatenation with a plus sign:\n\u0026amp;gt; \u0026amp;#39;foo\u0026amp;#39; + \u0026amp;#39;bar\u0026amp;#39; \u0026amp;#39;foobar\u0026amp;#39; This simple solution works just as well as the alternatives:\n// .join (common solution, in my experience) \u0026amp;gt; [\u0026amp;#39;foo\u0026amp;#39;, \u0026amp;#39;bar\u0026amp;#39;].join(\u0026amp;#39;\u0026amp;#39;) \u0026amp;#39;foobar\u0026amp;#39; // .concat \u0026amp;gt; \u0026amp;#39;foo\u0026amp;#39;.concat(\u0026amp;#39;bar\u0026amp;#39;) \u0026amp;#39;foobar\u0026amp;#39; // Interpolation \u0026amp;gt; `foo${\u0026amp;#39;bar\u0026amp;#39;}` \u0026amp;#39;foobar\u0026amp;#39; A plus sign is all you need.\n","permalink":"/tils/javascript-string-concatenation/","summary":"Here\u0026rsquo;s a periodic reminder that JavaScript supports string concatenation with a plus sign:\n\u0026gt; \u0026#39;foo\u0026#39; + \u0026#39;bar\u0026#39; \u0026#39;foobar\u0026#39; ","title":"JavaScript String Concatenation"},{"content":"Need an array of letters? Here\u0026amp;rsquo;s a trick:\n\u0026amp;gt; const alpha = [...\u0026amp;#39;abcdefghijklmnopqrstuvwxyz\u0026amp;#39;] This assigns the following:\n\u0026amp;gt; alpha [ \u0026amp;#39;a\u0026amp;#39;, \u0026amp;#39;b\u0026amp;#39;, \u0026amp;#39;c\u0026amp;#39;, \u0026amp;#39;d\u0026amp;#39;, \u0026amp;#39;e\u0026amp;#39;, \u0026amp;#39;f\u0026amp;#39;, \u0026amp;#39;g\u0026amp;#39;, \u0026amp;#39;h\u0026amp;#39;, \u0026amp;#39;i\u0026amp;#39;, \u0026amp;#39;j\u0026amp;#39;, \u0026amp;#39;k\u0026amp;#39;, \u0026amp;#39;l\u0026amp;#39;, \u0026amp;#39;m\u0026amp;#39;, \u0026amp;#39;n\u0026amp;#39;, \u0026amp;#39;o\u0026amp;#39;, \u0026amp;#39;p\u0026amp;#39;, \u0026amp;#39;q\u0026amp;#39;, \u0026amp;#39;r\u0026amp;#39;, \u0026amp;#39;s\u0026amp;#39;, \u0026amp;#39;t\u0026amp;#39;, \u0026amp;#39;u\u0026amp;#39;, \u0026amp;#39;v\u0026amp;#39;, \u0026amp;#39;w\u0026amp;#39;, \u0026amp;#39;x\u0026amp;#39;, \u0026amp;#39;y\u0026amp;#39;, \u0026amp;#39;z\u0026amp;#39; ] This is called spread syntax. Our string is iterable, and ... spreads them out into our array.\nMDN Spread …","permalink":"/tils/javascript-spread-string-into-array/","summary":"Need an array of letters? Here\u0026rsquo;s a trick:\n\u0026gt; const alpha = [...\u0026#39;abcdefghijklmnopqrstuvwxyz\u0026#39;] ","title":"JavaScript Spread a String Into An Array"},{"content":"When you join an array in JavaScript, the default separator is a comma:\nnode\u0026amp;gt; [\u0026amp;#39;j\u0026amp;#39;, \u0026amp;#39;a\u0026amp;#39;, \u0026amp;#39;k\u0026amp;#39;, \u0026amp;#39;e\u0026amp;#39;].join() \u0026amp;#39;j,a,k,e\u0026amp;#39; In production code, you often want to pass an empty string instead.\nnode\u0026amp;gt; [\u0026amp;#39;j\u0026amp;#39;, \u0026amp;#39;a\u0026amp;#39;, \u0026amp;#39;k\u0026amp;#39;, \u0026amp;#39;e\u0026amp;#39;].join(\u0026amp;#39;\u0026amp;#39;) \u0026amp;#39;jake\u0026amp;#39; This is something I relearn from time to time, because it works differently in Ruby.\nirb\u0026amp;gt; [\u0026amp;#34;j\u0026amp;#34;, \u0026amp;#34;a\u0026amp;#34;, \u0026amp;#34;k\u0026amp;#34;, \u0026amp;#34;e\u0026amp;#34;].join() =\u0026amp;gt; \u0026amp;#34;jake\u0026amp;#34; MDN Array Join Docs\n","permalink":"/tils/javascript-array-join-comma-default-separator/","summary":"When you join an array in JavaScript, the default separator is a comma:\nnode\u0026gt; [\u0026#39;j\u0026#39;, \u0026#39;a\u0026#39;, \u0026#39;k\u0026#39;, \u0026#39;e\u0026#39;].join() \u0026#39;j,a,k,e\u0026#39; ","title":"JavaScript Array Join Comma Default Separator"},{"content":"The JavaScript bitwise AND operator (\u0026amp;amp;) can be used for some real-world tasks, like testing if a number is odd or even.\nOdd-even checking might normally be done with a modulo and the number 2.\n// Checking 1 \u0026amp;gt; 1 % 2 === 0 ? \u0026amp;#34;Even\u0026amp;#34; : \u0026amp;#34;Odd\u0026amp;#34; \u0026amp;#39;Odd\u0026amp;#39; // Checking 2 \u0026amp;gt; 2 % 2 === 0 ? \u0026amp;#34;Even\u0026amp;#34; : \u0026amp;#34;Odd\u0026amp;#34; \u0026amp;#39;Even\u0026amp;#39; The bitwise AND is an alternative. When used between two numbers, it returns a new number that has 1\u0026amp;rsquo;s in each position where both numbers have a 1.\nYou can use this to compare any number to 1, which has a 1 in the first binary position. …","permalink":"/tils/javascript-bitwise-and/","summary":"The JavaScript bitwise AND operator (\u0026amp;) can be used for some real-world tasks, like testing if a number is odd or even.\n","title":"JavaScript Bitwise AND"},{"content":"I think many engineers would be better contributors if they started to think more about the customer impact of their work.\nLevel One: Delivery Early in our careers, we’re looking for explicit directions, which we learn to deliver, and that is vital.\nSophisticated checkout wizard? Complex domain model? I can do it! You learn to make it work and demonstrate that you can ship.\nNext Level: Thinking Like a Product Owner The next level is thinking like a product owner and asking: “Does this improve the customer experience?”\nWe worry that managers don’t want pushback and would rather hear “Can do.” …","permalink":"/posts/become-a-product-first-software-engineer/","summary":"I think many engineers would be better contributors if they started to think more about the customer impact of their work.\n","title":"Become a Product-First Software Engineer"},{"content":"Longtime Vim users know the vimtutor program, which teaches you Vim in Vim. Vim 9.2 shipped with a new :Tutor command that improves on this.\n:Tutor The original vimtutor is entered like this:\n$ vimtutor Digging into this script on my machine, we see:\n$VIM -f -u NONE -c \u0026amp;#34;set nocp showcmd\u0026amp;#34; \u0026amp;#34;$TUTORCOPY\u0026amp;#34; The flag -u NONE opens the tutor without any configurations applied. This can be useful but is also harsh, especially for experienced users or those coming from an IDE.\nThe new command is entered via command-mode:\n:Tutor This opens the Tutor in Vim, but with all your plugins and …","permalink":"/tils/vim-tutor-command/","summary":"Longtime Vim users know the vimtutor program, which teaches you Vim in Vim. Vim 9.2 shipped with a new :Tutor command that improves on this.\n:Tutor ","title":"Vim :Tutor Command"},{"content":"This page summarizes my usage of LLMs (\u0026amp;ldquo;AI\u0026amp;rdquo;) on this website.\nIf you\u0026amp;rsquo;d like to learn why I made this page, check out the AI Manifesto.\nSummary I hand-write every post about things I find interesting, and I get a lot out of it. I\u0026amp;rsquo;m not interested in delegating that process to AI. I use AI to improve what I already do.\nHow I Use AI Tools I use Grammarly to check for grammatical mistakes and typos.\nI use ChatGPT to help me iterate on my posts\u0026amp;rsquo; title and description, because it is sometimes better at describing what I\u0026amp;rsquo;ve written than I am.\nI use ChatGPT to …","permalink":"/ai-policy/","summary":"This page summarizes my usage of LLMs (\u0026ldquo;AI\u0026rdquo;) on this website.\n","title":"AI Policy"},{"content":"After reviewing a lot of pull requests, I\u0026amp;rsquo;ve settled on a simple default: if my comments are all nitpicks, suggestions, questions, or non-blocking issues, I leave them and approve the PR at the same time.\nHere\u0026amp;rsquo;s some detail on how it works. But first, two clarifying questions.\nWhy Leave Comments, If Approving? Why leave comments if I\u0026amp;rsquo;m approving?\nComments show that someone has thought about the problem and the solution, and cares about what\u0026amp;rsquo;s happening to the code. Occasionally, they offer a chance to learn or to surface misunderstandings, assumptions, or hidden risks. …","permalink":"/posts/on-commenting-and-approving-pull-requests/","summary":"After reviewing a lot of pull requests, I\u0026rsquo;ve settled on a simple default: if my comments are all nitpicks, suggestions, questions, or non-blocking issues, I leave them and approve the PR at the …","title":"On commenting and approving pull requests"},{"content":"When possible, I prefer to upgrade dependencies incrementally rather than making several version jumps. So, we try not to go from React 16 to 19. We go from 16 to 17, then to 18, and finally to 19.\nThe Risks of Jumping Multiple Versions It\u0026amp;rsquo;s very tempting to jump to the latest release. It feels like time travel; putting your application on the latest in the fewest number of steps. What’s not to like?\nThe danger of such a jump is that it\u0026amp;rsquo;s easy to miss information when it’s stacked. Multiple releases can contain multiple deprecations, sunsetted features, and scripts you were …","permalink":"/posts/incremental-dependency-upgrades/","summary":"When possible, I prefer to upgrade dependencies incrementally rather than making several version jumps. So, we try not to go from React 16 to 19. We go from 16 to 17, then to 18, and finally to 19. …","title":"Incremental Dependency Upgrades"},{"content":"A policy I favor with dependency management: go forward. Going backward is an exception that we must plan to remedy.\nExample Consider a CVE in a dependency at version 5.0 (v5.0). We’re advised that v6.0 and v4.0 are safe. Which way should we go, forward or backward?\nWe want to default to forward. Even if we\u0026amp;rsquo;ve been on v4.0 before and know it is compatible. Even if we aren’t sure that v6.0 is \u0026amp;ldquo;solid\u0026amp;rdquo; or if it takes some code changes to implement.\nMy reasoning here is that it puts it in a better place with OSS maintenance conventions. If another CVE is released, it’s likely to …","permalink":"/posts/dependencies-go-forward/","summary":"A policy I favor with dependency management: go forward. Going backward is an exception that we must plan to remedy.\nExample Consider a CVE in a dependency at version 5.0 (v5.0). We’re advised that …","title":"Dependencies Go Forward"},{"content":"If people can make catastrophic mistakes on your team, the process is broken.\nReal-World Example: Git Branch Protections An example is branch protections in a Git repository. These are rules that prevent engineers from deleting or altering history in shared branches, such as main, which on most teams would lead to a major problem.\nEngineers don\u0026amp;rsquo;t intend to delete or alter main, but it can happen when rifling through terminal commands. We\u0026amp;rsquo;re moving fast, and occasionally we press enter on the wrong command. As a contributor, I expect policies to catch me when I make this mistake. …","permalink":"/posts/catastrophic-mistakes-are-a-team-failure/","summary":"If people can make catastrophic mistakes on your team, the process is broken.\n","title":"Catastrophic Mistakes Are a Team Failure"},{"content":"Why do software teams push continuous deployment so strongly? One of the most important and least discussed reasons is engineer morale.\nEngineers Want to Build Engineers want to build.\nThey\u0026amp;rsquo;d much rather ship an experiment that proves or disproves a theory than discuss the theory. When you hire engineers who know how to ship, that\u0026amp;rsquo;s what they want to be doing.\nContinuous deployment allows that itch to be scratched by eliminating barriers to building. That makes engineers happy.\nIt Lowers Real and Perceived Risk Continuous deployment lowers risk, too— real and perceived.\nThe …","permalink":"/posts/ci-cd-and-engineer-morale/","summary":"Why do software teams push continuous deployment so strongly? One of the most important and least discussed reasons is engineer morale.\n","title":"CI/CD and Engineer Morale"},{"content":"Is a Docker container available on a registry? Here\u0026amp;rsquo;s how I\u0026amp;rsquo;d check, right from the CLI:\ndocker image ls \u0026amp;lt;container-name\u0026amp;gt; You could use this when doing an upgrade. Perhaps we\u0026amp;rsquo;re upgrading Debian to Bookworm, and we don\u0026amp;rsquo;t know which Python versions are available on our new OS version. This command will tell you.\n$ docker image ls python:3.10.12-slim-bookworm # Exists 😊 IMAGE ID DISK USAGE CONTENT SIZE EXTRA python:3.10.12-slim-bookworm 4d440b214e44 244MB 52.3MB $ docker image ls python:3.10-slim-bookworm # Doesn\u0026amp;#39;t exist 😔 IMAGE ID DISK USAGE CONTENT SIZE EXTRA …","permalink":"/tils/validate-a-container-with-docker-pull/","summary":"Is a Docker container available on a registry? Here\u0026rsquo;s how I\u0026rsquo;d check, right from the CLI:\ndocker image ls \u0026lt;container-name\u0026gt; ","title":"Validate a Container With Docker Ls"},{"content":"Permanently forking a library is something I\u0026amp;rsquo;ve observed on several teams. But, there are tradeoffs that aren\u0026amp;rsquo;t always obvious.\nWhen to Fork: My Mental Model Here\u0026amp;rsquo;s my mental model for evaluating when to fork:\nDead project: this is the easy one. If the software is open-source and has an accommodating license, this can be a great solution to the \u0026amp;ldquo;Build vs. Buy\u0026amp;rdquo; question. Capable team that can maintain it: Got a team of pros who want to maintain a fork of a popular library for years? This might work. The library won\u0026amp;rsquo;t merge or engage with your patch: You need …","permalink":"/posts/permanently-forking-a-library-tradeoffs/","summary":"Permanently forking a library is something I\u0026rsquo;ve observed on several teams. But, there are tradeoffs that aren\u0026rsquo;t always obvious.\n","title":"Tradeoffs of Permanently Forking a Library"},{"content":"A missing README is rarely just missing documentation. It usually signals other things, too.\nA project that’s going to be difficult to set up. When I pull down a project without a README, I’m starting from zero. What kind of app is this? What backend, frontend, persistence layer, and dependencies are involved? Has it ever been set up on my operating system? Answering these becomes homework I didn\u0026amp;rsquo;t ask for. A project whose purpose may not be clearly defined. What is this project? Why does it matter? Shaky domain language. What kind of users do we have (Customers and Employees, or Players …","permalink":"/posts/a-missing-readme-isnt-a-missing-readme/","summary":"A missing README is rarely just missing documentation. It usually signals other things, too.\n","title":"A Missing README Isn't a Missing README"},{"content":"When adding CI checks, you can gradually increase rigor while easing your codebase into a better place.\nMaybe you want to enforce auto-formatting with Prettier as part of your frontend code quality. This is the static analysis layer described by Kent C. Dodds\u0026#39; \u0026amp;ldquo;Testing Trophy.\u0026amp;rdquo;\nBut your codebase has never been formatted with this tool before, so none of the files meet the standard. This creates a tough choice: do you discard years of version control history and create potentially numerous merge conflicts with a huge formatting PR?\nThe gradual answer: when you touch the stylesheets, …","permalink":"/tils/ease-into-ci-rigor-with-globs/","summary":"When adding CI checks, you can gradually increase rigor while easing your codebase into a better place.\n","title":"Ease Into CI Rigor With Globs"},{"content":"I use the append \u0026amp;gt;\u0026amp;gt; and overwrite \u0026amp;gt; redirection operators all the time. Today I\u0026amp;rsquo;d like to share a simple mental model I\u0026amp;rsquo;ve developed for remembering which is which.\nAppend, \u0026amp;gt;\u0026amp;gt;, appends to its target with the redirected output. It\u0026amp;rsquo;s two carets, because it \u0026amp;ldquo;adds a second thing\u0026amp;rdquo; to what already exists.\nOverwrite, \u0026amp;gt;, replaces its target with the redirected output. It\u0026amp;rsquo;s one caret, because it \u0026amp;ldquo;becomes\u0026amp;rdquo; its target.\nHope this helps.\n","permalink":"/tils/overwrite-vs-append/","summary":"I use the append \u0026gt;\u0026gt; and overwrite \u0026gt; redirection operators all the time. Today I\u0026rsquo;d like to share a simple mental model I\u0026rsquo;ve developed for remembering which is which.\n","title":"Overwrite vs. Append"},{"content":"A developer experience practice I\u0026amp;rsquo;ve recently picked up is routine Friday reboots of my workstation. I think this can make your workflow (and even perhaps your life as an engineer?) a little better.\nDoes This Sound Like You? You haven\u0026amp;rsquo;t rebooted your workstation in weeks, and may be proud of this fact. You\u0026amp;rsquo;re maintaining legacy code. You find setting up these projects to be finicky. You have uncommitted changes that you have to remember to reapply to get the projects working. You resist reboots or create obstacles to them, like programs that won\u0026amp;rsquo;t close without …","permalink":"/posts/better-dev-exp-with-friday-reboots/","summary":"A developer experience practice I\u0026rsquo;ve recently picked up is routine Friday reboots of my workstation. I think this can make your workflow (and even perhaps your life as an engineer?) a little …","title":"Better Developer Experience With Friday Reboots"},{"content":"I\u0026amp;rsquo;ve been participating in fully-remote software standups every day for over a decade, and over the past two years, leading them, too. Here\u0026amp;rsquo;s how I run the best standup meetings that I can.\nGuiding Principle: Physically Stand Standups got their name because people physically stood up during them; use that as a guiding principle.\nI don\u0026amp;rsquo;t stand up, nor do people on my team, and not everyone can. But, the idea is to keep the meeting tight. Standups shouldn\u0026amp;rsquo;t be long. They should not be something you can step away from for ten minutes to flip the laundry.\nMeetings are …","permalink":"/posts/how-i-run-a-software-engineering-standup/","summary":"I\u0026rsquo;ve been participating in fully-remote software standups every day for over a decade, and over the past two years, leading them, too. Here\u0026rsquo;s how I run the best standup meetings that I …","title":"How I Run a Fully-Remote Software Engineering Standup"},{"content":"One of my favorite Git tricks is shoveling changes to a Git diff and then applying them later.\nI\u0026amp;rsquo;ll be in the middle of a tricky feature, spike, or bugfix, and need to switch gears. So, I redirect my unstaged changes to a diff:\ngit diff \u0026amp;gt; fantastic-feature.diff My global .gitignore ignores *.diff.\nLater, when I want those changes, I can usually run:\ngit apply fantastic-feature.diff The home directory of my project sometimes contains a dozen or more of these little diffs; things I tried or want to do someday.\nSure, you can use branches for this. I like applying diffs because I …","permalink":"/tils/git-apply-diff/","summary":"One of my favorite Git tricks is shoveling changes to a Git diff and then applying them later.\n","title":"Git Apply Diff"},{"content":"Do you want to read more? Over a decade, I\u0026amp;rsquo;ve been on a journey from casual to power-reader. In this post, I\u0026amp;rsquo;d like to share the ideas and techniques that have helped me read more.\nFirst, some background.\nMy Reading Journey I wasn\u0026amp;rsquo;t much of a reader as a kid; I was more interested in drawing and building things.\nToday, I read 40+ books per year. This always includes a few programming books, a habit that\u0026amp;rsquo;s rare in my field.\nBut forget the numbers: I love reading! It\u0026amp;rsquo;s meditation. It\u0026amp;rsquo;s helped me grow. And if you\u0026amp;rsquo;re here, I assume you want to do more of …","permalink":"/posts/how-i-read-44-books-last-year/","summary":"Do you want to read more? Over a decade, I\u0026rsquo;ve been on a journey from casual to power-reader. In this post, I\u0026rsquo;d like to share the ideas and techniques that have helped me read more.\n","title":"How I Read 44 Books Last Year"},{"content":"Docker gives you a way to control startup, while codifying that one container depends on others.\nAn example is most helpful here:\nservices: web: build: . depends_on: db: condition: service_healthy restart: true redis: condition: service_started redis: image: redis db: image: postgres:18 healthcheck: test: [\u0026amp;#34;CMD-SHELL\u0026amp;#34;, \u0026amp;#34;pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}\u0026amp;#34;] interval: 10s retries: 5 start_period: 30s timeout: 10s With this configuration, services are created in dependency order: web will come up only after redis is started and db healthy, as defined in the …","permalink":"/tils/docker-service-healthy/","summary":"Docker gives you a way to control startup, while codifying that one container depends on others.\n","title":"Docker Service Healthy"},{"content":"Need to override some Docker configuration locally? Too easy!\nDocker automatically reads an override file on your machine.\n\u0026amp;ldquo;By default, Compose reads two files, a compose.yaml and an optional compose.override.yaml file. By convention, the compose.yaml contains your base configuration. The override file can contain configuration overrides for existing services or entirely new services.\u0026amp;rdquo;\nEdit docker-compose.override.yml with your local overrides. I\u0026amp;rsquo;ve used this to pass environment variables in development with no code changes.\nDocs\n","permalink":"/tils/docker-compose-override/","summary":"Need to override some Docker configuration locally? Too easy!\n","title":"Docker Compose Override"},{"content":"I recently re-read It Doesn\u0026amp;rsquo;t Have to be Crazy at Work by Jason Fried and David Heinemeier of Basecamp. Here are some of my favorite ideas.\nI came up in the Ruby on Rails community, where Fried, DHH, and Basecamp loom large. I don\u0026amp;rsquo;t agree with everything they say. But, I\u0026amp;rsquo;ve always appreciated their articulate and passionate arguments about how technology teams can work better. I\u0026amp;rsquo;ve read every book they\u0026amp;rsquo;ve written.\nThese are the ideas that grabbed me on this read-through. I agree with them, except where noted, and they\u0026amp;rsquo;re \u0026amp;ldquo;strong opinions, loosely …","permalink":"/posts/favorite-ideas-from-idhtbcaw/","summary":"I recently re-read It Doesn\u0026rsquo;t Have to be Crazy at Work by Jason Fried and David Heinemeier of Basecamp. Here are some of my favorite ideas.\n","title":"Favorite Ideas from \"It Doesn't Have to Be Crazy at Work\""},{"content":"Vim-tiny is a great choice for remote server work.\nWhen I\u0026amp;rsquo;m working on a remote server with Vim (common use case: stripped-down Docker containers), I need a text editor to manipulate configuration files.\nvim-tiny is my current package of choice.\napt-get update apt-get install vim-tiny It\u0026amp;rsquo;s a basic version of Vim, no GUI, fewer features. Feels like a good call for servers without Vim, that we want to keep light and fast, that I may never return to.\nvim-tiny\n","permalink":"/tils/installing-vim-on-a-remote-server/","summary":"Vim-tiny is a great choice for remote server work.\n","title":"Installing Vim on a Remote Server"},{"content":"Most JavaScript projects come with a package.json. But what if you want to make a new one?\nThis blog\u0026amp;rsquo;s software is a great example. It\u0026amp;rsquo;s statically rendered with Hugo, but as a JavaScript dev, I want my JavaScript tooling, too, so I need a manifest.\nIf you install a single dependency, you\u0026amp;rsquo;ll get a package.json with that dependency. It works, but it\u0026amp;rsquo;s just a scaffold.\nThe better command is:\n$ npm init This starts a dialog where you can choose sensible defaults for package name, version, description, entry point, Git repository, author, and license. It scaffolds you a …","permalink":"/tils/initialize-with-npm-init/","summary":"Most JavaScript projects come with a package.json. But what if you want to make a new one?\n","title":"Initialize With npm init"},{"content":"One of my favorite problem-solving questions is: \u0026amp;ldquo;What\u0026amp;rsquo;s wrong with this idea?\u0026amp;rdquo; It drives the conversation away from why the idea might work, and toward why it might fail. That perspective is invaluable when building.\nQuestion: \u0026amp;ldquo;What\u0026amp;rsquo;s Wrong With This Idea?\u0026amp;rdquo; I learned this idea from Paul Arden:\n\u0026amp;ldquo;If, instead of seeking approval, you ask, \u0026amp;lsquo;What\u0026amp;rsquo;s wrong with it? How can I make it better?\u0026amp;rsquo;, you are more likely to get a truthful, critical answer.\u0026amp;quot;― Paul Arden, It\u0026amp;rsquo;s Not How Good You Are, It\u0026amp;rsquo;s How Good You Want To Be\nPeople …","permalink":"/posts/whats-wrong-with-this-idea/","summary":"One of my favorite problem-solving questions is: \u0026ldquo;What\u0026rsquo;s wrong with this idea?\u0026rdquo; It drives the conversation away from why the idea might work, and toward why it might fail. That …","title":"\"What's Wrong With This Idea?\""},{"content":"\u0026amp;ldquo;The server is 200 GiBs.\u0026amp;rdquo; \u0026amp;ldquo;Don\u0026amp;rsquo;t you mean GBs?\u0026amp;rdquo; \u0026amp;ldquo;No, I don\u0026amp;rsquo;t!\u0026amp;rdquo;\nGibibyte (GiBs) are a unit of measurement in computing that are used for storage. They are not gigabytes!\nGibibytes take their name from being like a gigabyte but being based on binary, hence the \u0026amp;ldquo;bi.\u0026amp;rdquo; Because information on a computer is stored in binary bits, they are a more purpose-built measurement for that data than GBs.\nOne GiB is equal to 1,073,741,824 bytes. Practically speaking, if you think in gigabytes like I do, then 1 GiB equals 1.07374 GB. So, they\u0026amp;rsquo;re …","permalink":"/tils/gibibyte-is-not-a-gigabyte/","summary":"\u0026ldquo;The server is 200 GiBs.\u0026rdquo; \u0026ldquo;Don\u0026rsquo;t you mean GBs?\u0026rdquo; \u0026ldquo;No, I don\u0026rsquo;t!\u0026rdquo;\n","title":"Gibibyte is Not a Gigabyte"},{"content":"I recently dug into the wonderful world of MySQL binlogs! Here a few learnings.\nBinlogs (binary logs) are log files that record everything your MySQL server does. They\u0026amp;rsquo;re used for replication or data recovery. Nice to have, but not necessary unless they\u0026amp;rsquo;re a defined part of your data recovery strategy.\nIn MySQL 8, binlogs are enabled by default with 30-day retention. Which can lead to many GiB-sized files.\nThis retention period can be adjusted like this (7-day retention being set here):\nSET GLOBAL binlog_expire_logs_seconds = 604800; If you happen to find lots of unneeded logs, you …","permalink":"/tils/mysql-binlogs/","summary":"I recently dug into the wonderful world of MySQL binlogs! Here a few learnings.\n","title":"MySQL Binlogs"},{"content":"When you see a list of numbers in a log or stack overflow, it\u0026amp;rsquo;s often ASCII (American Standard Code for Information Interchange)! Here\u0026amp;rsquo;s how to recognize and read it.\nSay you see this in a log:\n110 101 116 58 58 69 82 82 95 67 79 78 78 69 67 84 73 79 78 95 82 69 70 85 83 69 68 Don\u0026amp;rsquo;t panic! It\u0026amp;rsquo;s just ASCII. You might not need to translate it, but if you do, you can do so for free online.\nOr, here\u0026amp;rsquo;s some JavaScript that will get the job done, too!\n\u0026amp;gt; const ascii_code = \u0026amp;#39;110 101 116 58 58 69 82 82 95 67 79 78 78 69 67 84 73 79 78 95 82 69 70 85 83 69 68\u0026amp;#39; …","permalink":"/tils/recognizing-ascii/","summary":"When you see a list of numbers in a log or stack overflow, it\u0026rsquo;s often ASCII (American Standard Code for Information Interchange)! Here\u0026rsquo;s how to recognize and read it.\n","title":"Recognizing ASCII"},{"content":"I\u0026amp;rsquo;ve got a command docker on my machine. I need to upgrade it. So, who owns it?\nThis is always a bit of a detective story. The question is type:\n$ type -a docker docker is /usr/local/bin/docker \u0026amp;ldquo;If docker was a command, how would you interpret it?\u0026amp;rdquo; -a means \u0026amp;ldquo;List all occurrences.\u0026amp;rdquo; I still don\u0026amp;rsquo;t know who owns docker.\nNext, which:\n$ which -a docker /usr/local/bin/docker \u0026amp;ldquo;Locate this program and list all answers.\u0026amp;rdquo; Nothing new.\nFinally, ls -l:\n$ ls -l /usr/local/bin/docker /usr/local/bin/docker -\u0026amp;gt; …","permalink":"/tils/who-owns-this-program/","summary":"I\u0026rsquo;ve got a command docker on my machine. I need to upgrade it. So, who owns it?\n","title":"Who Owns This Program?"},{"content":"What is a programmer but a series of PRs (pull requests)? I optimize PRs to introduce the best code I can, be easy to review, and document my work so I can make sense of it in the future. Here are some things I always check before opening a PR.\nPassing Static Analyses All of the static analysis tools in my project must pass locally.\nOn a JavaScript project, that\u0026amp;rsquo;s an auto-formatter (Prettier), linter (ESLint), and type-checker (TypeScript). All of these run on files I touch automatically. If that\u0026amp;rsquo;s not feasible for you, a Husky pre-commit hook will do the job.\nPassing Tests All of …","permalink":"/posts/things-i-check-before-opening-a-pr/","summary":"What is a programmer but a series of PRs (pull requests)? I optimize PRs to introduce the best code I can, be easy to review, and document my work so I can make sense of it in the future. Here are …","title":"Things I Check Before Opening a PR"},{"content":"You might have heard that using blocking, waiting code, e.g., sleep in many languages, is discouraged. Why?\nIntroduction to Sleep Sleep is a blocking, waiting function present in some languages. Here\u0026amp;rsquo;s sleep in Ruby.\n# task.rb print \u0026amp;#34;Starting task... \u0026amp;#34; sleep 10 # Simulating some activity... puts \u0026amp;#34;done.\u0026amp;#34; The user experiences a printed output, with ten seconds of time between the ellipses and \u0026amp;ldquo;done.\u0026amp;rdquo;\n$ ruby task.rb Starting task... done. It\u0026amp;rsquo;s used when we want to wait for a task to be finished. Pretty cool, right?\nWell, there are two big problems with …","permalink":"/posts/why-we-discourage-sleep/","summary":"You might have heard that using blocking, waiting code, e.g., sleep in many languages, is discouraged. Why?\n","title":"Why sleep Is Risky in Production Code"},{"content":"Abstractions in code are easy to add and challenging to remove. Sometimes we feel stuck with them. But the good news is, you can back out of almost any abstraction with the right mindset.\nLet\u0026amp;rsquo;s look at an example.\nA Breaking Abstraction Imagine a currency formatter.\nfunction formatPrice(amount) { return `$${amount.toFixed(2)}` } formatPrice(10) // \u0026amp;#34;$10.00\u0026amp;#34; Soon our company decides to launch in Europe. Let\u0026amp;rsquo;s add that conditional.\nfunction formatPrice(amount, currency) { if (currency === \u0026amp;#34;EUR\u0026amp;#34;) return `€${amount.toFixed(2)}` return `$${amount.toFixed(2)}` } …","permalink":"/posts/how-to-unabstract-an-abstraction/","summary":"Abstractions in code are easy to add and challenging to remove. Sometimes we feel stuck with them. But the good news is, you can back out of almost any abstraction with the right mindset.\n","title":"How to Unabstract an Abstraction"},{"content":"How can I output the entire DOM to a file in a JavaScript test?\nTesting Library is amazing. But, one area I struggle with it: when writing high-level integration tests, when an assertion fails, the output can be noisy and incomplete. It makes debugging tough.\nThe test runner says something like:\n\u0026amp;ldquo;I couldn\u0026amp;rsquo;t find \u0026amp;lsquo;Submit\u0026amp;rsquo; in these two thousand lines of HTML, which are so big I\u0026amp;rsquo;ve truncated for you.\u0026amp;rdquo;\nWhere is \u0026amp;lsquo;Submit\u0026amp;rsquo;? Nobody knows.\nHere\u0026amp;rsquo;s the hack I found: print the entire DOM to a file with Node\u0026amp;rsquo;s File System module (fs).\nimport fs …","permalink":"/tils/writing-the-dom-with-fs/","summary":"How can I output the entire DOM to a file in a JavaScript test?\n","title":"Writing the DOM with fs"},{"content":"Git has two kinds of commands: \u0026amp;ldquo;porcelain\u0026amp;rdquo; and \u0026amp;ldquo;plumbing\u0026amp;rdquo;.\nI\u0026amp;rsquo;ve known these terms for a long time, but recently learned I was misusing them. It stops now!\n\u0026amp;ldquo;Porcelain\u0026amp;rdquo; commands are meant for end users to call. git-log, git-show, and git-bisect are a few.\n\u0026amp;ldquo;Plumbing\u0026amp;rdquo; commands are meant for scripts to call and are considered more stable. Commands you\u0026amp;rsquo;ve likely never heard of like git-update-ref and git-write-tree are plumbing commands.\nSee man git for more info.\n","permalink":"/tils/git-porcelain-and-plumbing/","summary":"Git has two kinds of commands: \u0026ldquo;porcelain\u0026rdquo; and \u0026ldquo;plumbing\u0026rdquo;.\n","title":"Git Porcelain and Plumbing 🚽"},{"content":"This year I built several Model Context Protocol (MCP) servers to support ongoing R\u0026amp;amp;D. Here\u0026amp;rsquo;s my report.\nBut first, some context. What is MCP, and why am I using it?\nWhy Am I Using MCP? MCP is an open-source standard for connecting AI applications to external systems. Here\u0026amp;rsquo;s some detail from a recent article:\n\u0026amp;ldquo;Mike Krieger, [MCP\u0026amp;rsquo;s] chief product officer, says the idea came while linking Claude, its chatbot, to services like Gmail, an email platform, and GitHub, a repository of code. Instead of integrating each application with Claude on a case-by-case basis, the …","permalink":"/posts/building-with-mcp-for-real/","summary":"This year I built several Model Context Protocol (MCP) servers to support ongoing R\u0026amp;D. Here\u0026rsquo;s my report.\n","title":"Building with MCP, for Real"},{"content":"If I had to start over as a person who wanted to become a professional programmer, in 2026, what would I do?\nI wrote about this before, in 2015, and am here in the present to report: a lot has changed. I\u0026amp;rsquo;d skip my old post, and read this one instead. It\u0026amp;rsquo;s the simplest, most viable advice I can give today.\nI\u0026amp;rsquo;m writing for someone who was like me a decade ago. You are interested in programming as a profession, have little or no programming experience, and you aren\u0026amp;rsquo;t sure what direction to go.\nThis is 100% my opinion. I hope it helps you.\nMy Advice First, this is …","permalink":"/posts/learning-to-program-in-2026/","summary":"If I had to start over as a person who wanted to become a professional programmer, in 2026, what would I do?\n","title":"Learning to Program in 2026"},{"content":"Here\u0026amp;rsquo;s a list of the things that I\u0026amp;rsquo;d like to learn about and have chosen not to specialize in (yet), as of 2026.\nI believe that there\u0026amp;rsquo;s value in exposing one\u0026amp;rsquo;s ignorance, or \u0026amp;ldquo;Wearing the white belt\u0026amp;rdquo; as it\u0026amp;rsquo;s described in Apprenticeship Patterns. Seniority isn\u0026amp;rsquo;t about accumulating all the skills. It\u0026amp;rsquo;s about being an expert where needed, dangerous everywhere else, relentlessly curious, and able to learn quickly.\nThis is a list of things that I don\u0026amp;rsquo;t know as of 2026. A caveat is that I know something about most things on this list. I can …","permalink":"/posts/things-i-dont-know-as-of-2026/","summary":"Here\u0026rsquo;s a list of the things that I\u0026rsquo;d like to learn about and have chosen not to specialize in (yet), as of 2026.\n","title":"Things I Don't Know, As of 2026"},{"content":"Today I accidentally I found a calculator on my computer, bc. Let\u0026amp;rsquo;s look at it!\nHere\u0026amp;rsquo;s how I entered the bc REPL.\nbc Here\u0026amp;rsquo;s me adding, a few different ways:\n# REPL! $ bc \u0026amp;gt;\u0026amp;gt;\u0026amp;gt; 1 + 1 2 # Eval! $ bc -e \u0026amp;#34;1 + 2\u0026amp;#34; 3 # File! $ cat add 1 + 3 $ bc -f add 4 I\u0026amp;rsquo;m reading the author\u0026amp;rsquo;s website and may have some more tips and tricks next time!\n","permalink":"/tils/calculating-with-bc/","summary":"Today I accidentally I found a calculator on my computer, bc. Let\u0026rsquo;s look at it!\n","title":"Calculating With Bc"},{"content":"There\u0026amp;rsquo;s an enviable quality of great engineers I\u0026amp;rsquo;ve known: they seem to get things right the first time. When you ask them to do something, and they say \u0026amp;ldquo;It\u0026amp;rsquo;s done\u0026amp;rdquo;, it is, almost always. How?\nHere, I think there actually is \u0026amp;ldquo;one weird trick.\u0026amp;rdquo; It\u0026amp;rsquo;s a bit tedious, but it compounds and is dead simple. It\u0026amp;rsquo;s checking your work before delivering.\nAgile Foundations I did not invent this idea. It\u0026amp;rsquo;s core to many folktales and fables, and also to Agile software development.\nMany Agile methodologies prescribe the concept of …","permalink":"/posts/getting-it-right-the-first-time/","summary":"There\u0026rsquo;s an enviable quality of great engineers I\u0026rsquo;ve known: they seem to get things right the first time. When you ask them to do something, and they say \u0026ldquo;It\u0026rsquo;s done\u0026rdquo;, it …","title":"Getting It Right the First Time"},{"content":"I\u0026amp;rsquo;ve been hacking on a TypeScript file all day, but I need transpiled JavaScript for any testing. How did I reduce cycle time (and mitigate many chances to forget to build at all)?\nMy solution was a neat package called nodemon. With it, I set up a rebuild on write:\nnodemon --watch src/index.ts --exec \u0026amp;#34;npm run build\u0026amp;#34; When I write the file, I get a new build in seconds. Very cool! 🕶️\n","permalink":"/tils/rebuilds-on-write-with-nodemon/","summary":"I\u0026rsquo;ve been hacking on a TypeScript file all day, but I need transpiled JavaScript for any testing. How did I reduce cycle time (and mitigate many chances to forget to build at all)?\n","title":"Rebuilds on Write With Nodemon"},{"content":"In MCP server code using stdio, you can\u0026amp;rsquo;t use any functions that print to stdout.\nIn TypeScript, this means:\nconsole.log(errorMessage) // 🛑 Bad console.error(errorMessage) // 🟢 Good! The stdio transport only supports valid MCP messages. None of these stdout-conversationalists are allowed:\nprint() statements in Python console.log() in JavaScript fmt.Println() in Go A smell that you\u0026amp;rsquo;ve broken this rule are messages like this in your MCP server log:\n==\u0026amp;gt; /Claude/mcp.log \u0026amp;lt;== 2025-12-30T16:31:32.702Z [error] [supercool-proj] Unexpected token \u0026amp;#39;I\u0026amp;#39;, \u0026amp;#34;I \u0026amp;#34;... is not …","permalink":"/tils/mcp-stdio-and-stdout/","summary":"In MCP server code using stdio, you can\u0026rsquo;t use any functions that print to stdout.\n","title":"MCP Stdio and Stdout"},{"content":"Watch and re-transpile any TypeScript file with:\ntsc file.ts --watch You can use this tool to quickly tell if you\u0026amp;rsquo;ve written compilable TypeScript, without other tooling set up. Take any TypeScript file and compile it with the watch option:\ntsc tutorial/src/index.ts --watch This returns:\nStarting compilation in watch mode. Now, when you write your tutorial file, the compiler transpiles, giving near-instant feedback.\nSee tsc --help for more info.\n","permalink":"/tils/tsc-watch/","summary":"Watch and re-transpile any TypeScript file with:\ntsc file.ts --watch ","title":"tsc watch"},{"content":"Throwing a JavaScript Error test to your observability tool? There\u0026amp;rsquo;s a better way.\nThrowing JS errors crashes the application, and even in testing this can be disruptive. Who wants to see a white screen? It\u0026amp;rsquo;s also unnecessary; use your observability library instead!\nInstead of this:\nthrow new Error(\u0026amp;#39;Aggressively testing Sentry\u0026amp;#39;) Do this (Sentry example):\nSentry.captureException(new Error(\u0026amp;#39;Quietly testing Sentry\u0026amp;#39;)); This logs an exception with no crash.\n","permalink":"/tils/observable-exceptions-that-dont-crash/","summary":"Throwing a JavaScript Error test to your observability tool? There\u0026rsquo;s a better way.\n","title":"Observable Exceptions That Don't Crash"},{"content":"Suppose your package.json specifies a Node engine of greater than or equal 24. Does this mean all engineers on your team will use Node 24? No, unless you use this one weird trick!\nLocally, the engines key of package.json is an advisory.\nTo force developers to use the same Node version, you can set the engine-strict flag. When true, such as through an .npmrc:\n# .npmrc engine-strict=true You\u0026amp;rsquo;ll get a nice error like this when you aren\u0026amp;rsquo;t using a compatible Node:\n$ npm install npm ERR! code EBADENGINE npm ERR! engine Unsupported engine npm ERR! engine Not compatible with your version …","permalink":"/tils/npm-install-engine-strict/","summary":"Suppose your package.json specifies a Node engine of greater than or equal 24. Does this mean all engineers on your team will use Node 24? No, unless you use this one weird trick!\n","title":"npmrc \"engine-strict\""},{"content":"Hello World! Today I\u0026amp;rsquo;m posting a TIL using MCP (Model Context Protocol).\nI created this TIL with Claude Desktop and a local MCP server, which generated a markdown file. What did I learn? All of the steps leading up to the words you\u0026amp;rsquo;re reading now.\nMCP docs\n","permalink":"/tils/hello-world-from-mcp/","summary":"Hello World! Today I\u0026rsquo;m posting a TIL using MCP (Model Context Protocol).\n","title":"Hello World from MCP!"},{"content":"I\u0026amp;rsquo;ve been known to cat README.md | pbcopy a file. There\u0026amp;rsquo;s a better way: the input redirection operator!\nMy command says this: \u0026amp;ldquo;Concatenate the README, then send the output of that to the pbcopy program.\u0026amp;rdquo; In the end, the README contents are in my paste buffer.\nHere\u0026amp;rsquo;s the better command:\npbcopy \u0026amp;lt; README.md This uses the input redirection operator, \u0026amp;lt;, to send the README contents into pbcopy. No concatenation, no pipe, just markdown straight to the program.\nSome might call my first example a Useless Use of Cat, because we aren\u0026amp;rsquo;t concatenating anything, and …","permalink":"/tils/input-redirection-operator/","summary":"I\u0026rsquo;ve been known to cat README.md | pbcopy a file. There\u0026rsquo;s a better way: the input redirection operator!\n","title":"Input Redirection Operator"},{"content":"To toggle back and forth between two Tmux sessions, use \u0026amp;lt;leader\u0026amp;gt;-L.\n","permalink":"/tils/tmux-toggle-sessions/","summary":"To toggle back and forth between two Tmux sessions, use \u0026lt;leader\u0026gt;-L.\n","title":"Tmux Toggle Sessions"},{"content":"Django Rest Framework includes a UI for interacting with your API. Here\u0026amp;rsquo;s how to turn it off.\nThis is a screenshot of the UI from the docs.\nIt\u0026amp;rsquo;s pretty cool! But, I don\u0026amp;rsquo;t want it in production. Add this to your settings file to force JSON rendering in non-debug mode\u0026amp;ndash; Django-speak for production.\n# settings.py if not DEBUG: REST_FRAMEWORK[\u0026amp;#34;DEFAULT_RENDERER_CLASSES\u0026amp;#34;] = [ \u0026amp;#34;rest_framework.renderers.JSONRenderer\u0026amp;#34;, ] This will render a JSON object instead of the UI.\n","permalink":"/tils/disable-drf-browsable-api/","summary":"Django Rest Framework includes a UI for interacting with your API. Here\u0026rsquo;s how to turn it off.\n","title":"Disable DRF Browsable API"},{"content":"I\u0026amp;rsquo;ve written before about the two competing ways to import React, but after React 17, you don\u0026amp;rsquo;t need either.\nReact used to be mostly imported as a default:\nimport React from \u0026amp;#39;react\u0026amp;#39;; Then, we were advised by core team members to drop the default because it was going to be deprecated:\nimport * as React from \u0026amp;#39;react\u0026amp;#39;; I\u0026amp;rsquo;m late to the party learning that with the JSX transform introduced in React 17, you don\u0026amp;rsquo;t need to import React at all to use JSX. You only need to import other functions you might use.\nimport { useState } from \u0026amp;#39;react\u0026amp;#39;; The best code …","permalink":"/tils/import-react-and-react-17-jsx-transform/","summary":"I\u0026rsquo;ve written before about the two competing ways to import React, but after React 17, you don\u0026rsquo;t need either.\n","title":"Import React and React 17 JSX Transform"},{"content":"This year I\u0026amp;rsquo;ve run over 25 Scrum refinement meetings; here\u0026amp;rsquo;s what I\u0026amp;rsquo;ve learned.\nThis post will cover:\nWhat is refinement, and why does it matter? Two counterarguments, refuted! My step-by-step approach Notes on pointing bugs Let\u0026amp;rsquo;s dive in.\nWhat is Refinement? (Backlog) Refinement is a process of improving tickets so they are understood by the whole team and ready for work.\nRefinement turns chaff like this:\n\u0026amp;ldquo;Design for error on download screen\u0026amp;rdquo;\nInto wheat like this:\nGiven my server isn\u0026amp;#39;t configured for downloading assets And I\u0026amp;#39;m viewing the Download …","permalink":"/posts/running-great-refinement-meetings/","summary":"This year I\u0026rsquo;ve run over 25 Scrum refinement meetings; here\u0026rsquo;s what I\u0026rsquo;ve learned.\n","title":"Running Great Refinement Meetings"},{"content":"Sometimes your Docker build is cached, and you need to build without cache.\nSymptoms of this predicament: you build the container, take it down and up, and don\u0026amp;rsquo;t see your changes. And maybe see outdated files on the container filesystem.\nBuild without cache:\ndocker compose build --no-cache ","permalink":"/tils/docker-build-no-cache/","summary":"Sometimes your Docker build is cached, and you need to build without cache.\n","title":"Docker Build No Cache"},{"content":"Git prevents branches with the same name. But sometimes, I want to re-use a name! Here\u0026amp;rsquo;s my workaround.\nContext: I use Bitbucket to generate branch names. It will give me a branch for a feature \u0026amp;ldquo;User logs in\u0026amp;rdquo; named feature/user-logs-in. That name helps me convey what a branch\u0026amp;rsquo;s intentions are.\nBut what happens if I open another branch for that feature? Do I have pick a new name, just because Git says so? Not really.\nMy solution is to append the time since epoch, with date.\n$ git checkout -b feature/user-logs-in-$(date +%s) Branches all day:\n$ git checkout -b …","permalink":"/tils/timestamped-branch/","summary":"Git prevents branches with the same name. But sometimes, I want to re-use a name! Here\u0026rsquo;s my workaround.\n","title":"Timestamped Branch"},{"content":"\u0026amp;ldquo;Keep binaries out of version control\u0026amp;rdquo; is a truth I\u0026amp;rsquo;ve always held, but I recently learned more about why.\nYes, you don\u0026amp;rsquo;t want large files in version control, which binaries often are.\nYes, you shouldn\u0026amp;rsquo;t version changes you can\u0026amp;rsquo;t review, which binary changes are.\nBut even worse, binaries can make your Git repo explode in size over time. Even when your \u0026amp;ldquo;remove\u0026amp;rdquo; (quotes intentional) or replace them with new ones, they are part of the repo. They\u0026amp;rsquo;re cloned every time the repo is cloned, slowing down the developer experience, and even …","permalink":"/tils/why-keep-binaries-out-of-version-control/","summary":"\u0026ldquo;Keep binaries out of version control\u0026rdquo; is a truth I\u0026rsquo;ve always held, but I recently learned more about why.\n","title":"Why Keep Binaries Out of Version Control"},{"content":"My site\u0026amp;rsquo;s build script has a set command; but what does it do?\n#!/usr/bin/env bash set -euo pipefail hugo --environment production --cleanDestinationDir git add public Here\u0026amp;rsquo;s the breakdown of these flags:\n-e: Exit immediately if a command exits with a non-zero status -u: Treat unset variables as an error when substituting -o pipefail: (\u0026amp;ldquo;Option pipefail\u0026amp;rdquo;) The return value of a pipeline is the status of the last command to exit with a non-zero status, or zero if no command exited with a non-zero status These work together to make your script fail fast. As a sentence, they …","permalink":"/tils/set-euo-pipefail/","summary":"My site\u0026rsquo;s build script has a set command; but what does it do?\n","title":"Set -euo pipefail"},{"content":"Working with me? Awesome! I built this user manual to share my working style and communication preferences.\nFor those interested in building their own user manual, here\u0026amp;rsquo;s the guide that helped me.\nWork What is your work and life setup? I start work at 9 AM EST. As a team lead, I have a morning routine that includes focusing my day\u0026amp;rsquo;s priorities, reviewing PR\u0026amp;rsquo;s and tickets, moving my personal tickets work forward, reading emails, and following up on requests that I\u0026amp;rsquo;ve made.\nAfter that, standups, meetings, pairing and mobbing, and working on my personal tickets fill my …","permalink":"/user-manual/","summary":"Working with me? Awesome! I built this user manual to share my working style and communication preferences.\nFor those interested in building their own user manual, here\u0026rsquo;s the guide that helped …","title":"User Manual"},{"content":"Something I\u0026amp;rsquo;ve taken a while to get under my fingers is Django\u0026amp;rsquo;s URL routing conventions. Here\u0026amp;rsquo;s a brief summary.\nAt the root of your app or apps, you\u0026amp;rsquo;ll find a file like this.\n# urls.py from django.urls import path from . import views urlpatterns = [ path(\u0026amp;#34;articles/2003/\u0026amp;#34;, views.special_case_2003), path(\u0026amp;#34;articles/\u0026amp;lt;int:year\u0026amp;gt;/\u0026amp;#34;, views.year_archive), path(\u0026amp;#34;articles/\u0026amp;lt;int:year\u0026amp;gt;/\u0026amp;lt;int:month\u0026amp;gt;/\u0026amp;#34;, views.month_archive), ] On a bigger app, these path calls might look like this:\npath(\u0026amp;#34;api/posts/\u0026amp;#34;, include(posts_urls.urlpatterns)), …","permalink":"/tils/django-routing-101/","summary":"Something I\u0026rsquo;ve taken a while to get under my fingers is Django\u0026rsquo;s URL routing conventions. Here\u0026rsquo;s a brief summary.\n","title":"Django Routing 101"},{"content":"Here\u0026amp;rsquo;s how you tail a Docker log.\ndocker logs --follow \u0026amp;lt;container_name\u0026amp;gt; See docker logs --help for more.\n","permalink":"/tils/tail-docker-logs/","summary":"Here\u0026rsquo;s how you tail a Docker log.\ndocker logs --follow \u0026lt;container_name\u0026gt; See docker logs --help for more.\n","title":"Tail Docker Logs"},{"content":"MySQL\u0026amp;rsquo;s display is tabular:\nmysql\u0026amp;gt; select id, first_name, last_name from web_user; +----+------------+-----------+ | id | first_name | last_name | +----+------------+-----------+ | 1 | Jake | Worth | +----+------------+-----------+ 1 row in set (0.00 sec) I very often prefer vertical results, which I can add with the \\G flag at the end.\nmysql\u0026amp;gt; select id, first_name, last_name from web_user\\G *************************** 1. row *************************** id: 1 first_name: Jake last_name: Worth 1 row in set (0.00 sec) Make sure you leave off the semicolon; \\G is a delimiter and the …","permalink":"/tils/mysql-vertical-result/","summary":"MySQL\u0026rsquo;s display is tabular:\n","title":"MySQL Vertical Result"},{"content":"Anytime you\u0026amp;rsquo;re making a list at work, rank it.\nThis idea came from my recent post:\nAnytime you\u0026amp;rsquo;re making a list at work, of anything, rank it. Tech debt? Security concerns? Feature requests? Rank them. Rank them so that you do the important ones first. Those are the only ones you probably will do.\n— Jake Worth ⚛️🌲 (@jakeworth.com) Oct 16, 2025 at 12:11 PM\nThis is a nuanced argument; let me try and make it!\nWhy Ranking Beats Lists Lists are good. But when execution matters, most lists are improved by ranking.\nConsider a list of technical debt problems. Maybe you have three you can …","permalink":"/posts/always-be-ranking/","summary":"Anytime you\u0026rsquo;re making a list at work, rank it.\n","title":"Prioritization for Engineers: Rank Every Work List"},{"content":"Acceptance criteria, or AC, describe what a feature or bugfix does. Writing them is an art, and some AC work much better than others. So, how do we make them work? By including a little more detail.\nLet\u0026amp;rsquo;s explore the goals of AC, a standard example, an example that works better, a few counterarguments, and how to write great AC with ease.\nGoals of AC: \u0026amp;ldquo;Right Thing\u0026amp;rdquo;, \u0026amp;ldquo;Thing Right\u0026amp;rdquo;, Documentation What are the goals of AC? They should:\nHelp us build the \u0026amp;ldquo;right thing\u0026amp;rdquo; Help us build the \u0026amp;ldquo;thing right\u0026amp;rdquo; Document the thing Build the \u0026amp;ldquo;right …","permalink":"/posts/acceptance-criteria-that-actually-work/","summary":"Acceptance criteria, or AC, describe what a feature or bugfix does. Writing them is an art, and some AC work much better than others. So, how do we make them work? By including a little more detail.\n","title":"Acceptance Criteria That Actually Work"},{"content":"Shift to a proactive mindset by making predictions before any experiments in a debugging session.\nThis may sound simple, but it\u0026amp;rsquo;s powerful. Here\u0026amp;rsquo;s how and why it works, why it moves you from reactive to proactive, and how to use it outside of debugging.\nHow and Why to Make a Prediction Imagine you\u0026amp;rsquo;re debugging a web application, and you\u0026amp;rsquo;re trying to understand why some data on your page looks wrong. Think of an action you might take to learn more.\nMaybe you decide to log your data to see what\u0026amp;rsquo;s wrong with it.\nBefore looking at your console to read the data, stop! …","permalink":"/posts/get-better-at-debugging-by-making-predictions/","summary":"Shift to a proactive mindset by making predictions before any experiments in a debugging session.\n","title":"Get Better at Debugging by Making Predictions"},{"content":"One of my projects features a Dockerized API. The dev process was: write a file, rebuild container, and restart container. There\u0026amp;rsquo;s a better way!\nAdd this to your docker-compose.yml (path and target directories will be unique to your project):\ndevelop: watch: - path: ./ target: /app/ action: sync+restart Then, start your container with --watch.\ndocker-compose up --watch api Now, anytime I write a file, Docker quickly copies it into my target directory and restarts the container. This reduced iterations from over a minute to under ten seconds.\nDocs\n","permalink":"/tils/docker-compose-watch/","summary":"One of my projects features a Dockerized API. The dev process was: write a file, rebuild container, and restart container. There\u0026rsquo;s a better way!\n","title":"Docker Compose Watch"},{"content":"Consider this scenario. You\u0026amp;rsquo;ve been given some work, and you\u0026amp;rsquo;re stuck. Hours have become days. You\u0026amp;rsquo;ve exhausted the internet. You\u0026amp;rsquo;re starting to backtrack, delete work, and start over. Feeling defeated. To make matters worse, you haven\u0026amp;rsquo;t told anybody.\nMaybe you\u0026amp;rsquo;ve waved off help and given a few rosy status reports: \u0026amp;ldquo;I\u0026amp;rsquo;ve got this! Just need one more day!\u0026amp;rdquo;\nYou\u0026amp;rsquo;re in deep. You know you\u0026amp;rsquo;ve spent too much time, but you keep pushing, into nights and weekends. And that\u0026amp;rsquo;s when you realize that you have to tell people how stuck …","permalink":"/posts/raise-the-im-stuck-flag/","summary":"Consider this scenario. You\u0026rsquo;ve been given some work, and you\u0026rsquo;re stuck. Hours have become days. You\u0026rsquo;ve exhausted the internet. You\u0026rsquo;re starting to backtrack, delete work, and …","title":"Raise the \"I'm Stuck\" Flag"},{"content":"Today I want to talk about a way I think about debugging: as a story that we tell to ourselves and each other.\nWe often face bugs without much information. I\u0026amp;rsquo;m talking about errors like this:\n$ npm run build The \u0026amp;#39;compilation\u0026amp;#39; argument must be an instance of Compilation What? This error comes from my work, and changes that I made caused it. I pushed a change to our codebase that removed a flag hiding errors. When the errors were exposed, we had to upgrade dependencies. Along the way, I broke the build, and no one knew it for a while.\nWhat\u0026amp;rsquo;s our path forward? First step: …","permalink":"/posts/debugging-is-a-story/","summary":"Today I want to talk about a way I think about debugging: as a story that we tell to ourselves and each other.\n","title":"Debugging Is a Story"},{"content":"Want to learn a tricky topic and sharpen your learning skills at the same time? In this post, I\u0026amp;rsquo;ll use the Feynman Learning Technique— a method of learning complex things by explaining them simply— with a sprinkle of LLM magic, to deepen my understanding of JavaScript promises.\nWhat is the Feynman Learning Technique? The Feynman Learning Technique is a learning method defined by Nobel laureate physicist and bongo drummer Richard Feynman. My perception is that it\u0026amp;rsquo;s been pieced together from things that Dr. Feynman said over time.\nFeynman\u0026amp;rsquo;s technique comprises four steps: …","permalink":"/posts/learning-javascript-promises-the-feynman-way/","summary":"Want to learn a tricky topic and sharpen your learning skills at the same time? In this post, I\u0026rsquo;ll use the Feynman Learning Technique— a method of learning complex things by explaining them …","title":"Learning JavaScript Promises the Feynman Way"},{"content":"I\u0026amp;rsquo;m proud to announce the launch of my digital guide to software debugging, Don\u0026amp;rsquo;t Stay Stuck: A Debugging Guide for Rising Engineers!\nI\u0026amp;rsquo;ve been working on this guide on nights and weekends, and it\u0026amp;rsquo;s the distillation of a decade of debugging triumph and tragedy.\nProduct Pitch Have you ever been stuck on a tough bug and felt you had no path forward? You\u0026amp;rsquo;ve tried everything you can imagine, and nothing is working? And then: a sharp engineer drops into the session, asks a few questions, and fixes the bug almost immediately! What is this superpower?\nDebugging is one of …","permalink":"/posts/dont-stay-stuck-digital-guide-launch/","summary":"I\u0026rsquo;m proud to announce the launch of my digital guide to software debugging, Don\u0026rsquo;t Stay Stuck: A Debugging Guide for Rising Engineers!\n","title":"\"Don't Stay Stuck\" Digital Guide Launch"},{"content":"Today I gave a talk to my team about the Twelve-Factor App Methodology.\nThe Twelve-Factor app is an important methodology for building software-as-a-service drafted by developers at Heroku in 2011. It outlines best practices for app setup, portability, deployment, and scalability.\nI chose to share this because I think it\u0026amp;rsquo;s relevant to all software engineers, and part of our shared language and conventions. And, I want the teams I work with to follow it, and for each of us to be a champion of its conventions.\nThe slides are here.\n","permalink":"/posts/a-talk-on-12-factors/","summary":"Today I gave a talk to my team about the Twelve-Factor App Methodology.\n","title":"A Talk on Twelve Factors"},{"content":"Sometimes I\u0026amp;rsquo;ll be looking at a Git commit online, and want to put my dev environment in a world where the commit is un-applied. This can be achieved by applying it in reverse.\ngit show \u0026amp;lt;sha\u0026amp;gt; | git apply -R Same behavior as git revert, but without the revert commit.\n","permalink":"/tils/git-apply-a-commit-in-reverse/","summary":"Sometimes I\u0026rsquo;ll be looking at a Git commit online, and want to put my dev environment in a world where the commit is un-applied. This can be achieved by applying it in reverse.\n","title":"Git Apply a Commit in Reverse"},{"content":"I\u0026amp;rsquo;ve been buying keyboards with Cherry MX switches for a decade, browns and a few blues. Today, I learned that MX stands for \u0026amp;ldquo;Mechanical X-Point\u0026amp;rdquo;– \u0026amp;ldquo;X\u0026amp;rdquo; for the x-shape of the keyboard stem you can see when you remove the keycap.\nhttps://en.wikipedia.org/wiki/Cherry_AG\n","permalink":"/tils/origin-of-the-term-cherry-mx/","summary":"I\u0026rsquo;ve been buying keyboards with Cherry MX switches for a decade, browns and a few blues. Today, I learned that MX stands for \u0026ldquo;Mechanical X-Point\u0026rdquo;– \u0026ldquo;X\u0026rdquo; for the x-shape of …","title":"Origin of the Term Cherry MX"},{"content":"As a long-time Ruby on Rails programmer, I thought that the name ActiveRecord –the model layer of Rails\u0026amp;rsquo; MVC– was branding. I didn\u0026amp;rsquo;t know that it\u0026amp;rsquo;s an architectural pattern, described by Martin Fowler in the 2003 book Patterns of Enterprise Application Architecture.\nAn object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.\nhttps://www.martinfowler.com/eaaCatalog/activeRecord.html\n","permalink":"/tils/origin-of-activerecord/","summary":"As a long-time Ruby on Rails programmer, I thought that the name ActiveRecord –the model layer of Rails\u0026rsquo; MVC– was branding. I didn\u0026rsquo;t know that it\u0026rsquo;s an architectural pattern, …","title":"Origin of ActiveRecord"},{"content":"The Node REPL has its own editor:\nnode \u0026amp;gt; .editor // Entering editor mode (Ctrl+D to finish, Ctrl+C to cancel) const increment = (array) =\u0026amp;gt; array.map(item =\u0026amp;gt; item + 1) increment([1,2,3]) Exit and evaluate with Ctrl+D:\n[ 2, 3, 4 ] I\u0026amp;rsquo;d use this to experiment with JavaScript API\u0026amp;rsquo;s without leaving my terminal-based IDE.\n","permalink":"/tils/editing-in-nodes-repl-editor/","summary":"The Node REPL has its own editor:\nnode \u0026gt; .editor // Entering editor mode (Ctrl+D to finish, Ctrl+C to cancel) const increment = (array) =\u0026gt; array.map(item =\u0026gt; item + 1) increment([1,2,3]) Exit …","title":"Editing in Nodes REPL Editor"},{"content":"I have a Vim normal mode mapping, and I don\u0026amp;rsquo;t know where it comes from. My Vim config, a plugin, somewhere else? nmap shows me the mapping:\n:nmap gsp n gsp * 1z= And verbose nmap shows me where it comes from:\n:verbose nmap gsp n gsp * 1z= Last set from ~/.vimrc line 111 The setting is coming from inside the .vimrc!\n","permalink":"/tils/vim-mappings-verbose/","summary":"I have a Vim normal mode mapping, and I don\u0026rsquo;t know where it comes from. My Vim config, a plugin, somewhere else? nmap shows me the mapping:\n:nmap gsp n gsp * 1z= And verbose nmap shows me where …","title":"Vim Mappings Verbose"},{"content":"It\u0026amp;rsquo;s common practice to create usernames in a non-production database like \u0026amp;ldquo;dev@example.com\u0026amp;rdquo; or \u0026amp;ldquo;customer@example.org\u0026amp;rdquo;. We do this to say: \u0026amp;ldquo;This is just an example!\u0026amp;rdquo; But there\u0026amp;rsquo;s a more to the story.\nexample.com and example.org are IANA-managed reserved domains, maintained for documentation purposes. So, not only are they a communicative choice, they\u0026amp;rsquo;re supported for these use cases without any prior coordination.\nhttps://www.iana.org/domains/reserved\n","permalink":"/tils/example-dot-com-in-your-data/","summary":"It\u0026rsquo;s common practice to create usernames in a non-production database like \u0026ldquo;dev@example.com\u0026rdquo; or \u0026ldquo;customer@example.org\u0026rdquo;. We do this to say: \u0026ldquo;This is just an …","title":"Example.com in Your Data"},{"content":"Today I learned about the .har extension (via Wikipedia):\nThe HTTP Archive format, or HAR, is a JSON-formatted archive file format for logging of a web browser\u0026amp;rsquo;s interaction with a site. The common extension for these files is .har.\nIn Chrome DevTools\u0026amp;rsquo; network tab you can export a .har file with your current browser\u0026amp;rsquo;s interaction. And that same extension can be uploaded back into the network tab. It\u0026amp;rsquo;s an information-rich way to communicate what you see in the network tab.\n","permalink":"/tils/har-files-in-chrome/","summary":"Today I learned about the .har extension (via Wikipedia):\n","title":"Har Files in Chrome"},{"content":"Confluence was messy. Our documentation felt outdated, hard to navigate, and unreliable. Rather than scrap everything and start over, I decided to try something different: a Kaizen.\nHere\u0026amp;rsquo;s what we did, what we learned, and how it went.\nWhat is a Kaizen? Kaizen is a Japanese business philosophy that can be translated to mean \u0026amp;ldquo;Change for the better.\u0026amp;rdquo; It can be an event, too.\nBefore this experiment, I\u0026amp;rsquo;d observed a Kaizen workshop once in a factory, but I\u0026amp;rsquo;d never led one myself, or heard of one done on a software team.\nWhy Run a Kaizen (on a Software Team)? In …","permalink":"/posts/we-ran-a-software-engineering-kaizen/","summary":"Confluence was messy. Our documentation felt outdated, hard to navigate, and unreliable. Rather than scrap everything and start over, I decided to try something different: a Kaizen.\n","title":"A Kaizen for Knowledge Work"},{"content":"A suggestion about data: generate it now, and figure out curation later.\nExample: Labeling Tickets Here\u0026amp;rsquo;s an example from my work. When I write software engineering tickets, I label them; if I\u0026amp;rsquo;m writing a ticket that improves patching, I\u0026amp;rsquo;d label it \u0026amp;ldquo;security.\u0026amp;rdquo;\nIn Jira, those labels are links. Click \u0026amp;ldquo;security\u0026amp;rdquo; and you\u0026amp;rsquo;ll see dozens of tickets that we\u0026amp;rsquo;ve written to improve our security posture. And so, when a stakeholder asks: \u0026amp;ldquo;What are we doing about security?\u0026amp;rdquo;\u0026amp;ndash; we have an answer. All because we continually applied one …","permalink":"/posts/generate-the-data/","summary":"A suggestion about data: generate it now, and figure out curation later.\n","title":"Generate the Data. Worry Later."},{"content":" Pastures which are subject to overflow should be avoided. All lambs born in April or May should be drenched about August or September following, to be certain of ridding them of worms that may later cause their death.\nThis is output from a tool I built called Veterinary Ipsum. It\u0026amp;rsquo;s something I\u0026amp;rsquo;ve created a few times: a Lorem Ipsum generator specific to my company\u0026amp;rsquo;s business domain.\nWhat is Lorem Ipsum? Lorem Ipsum is placeholder text used to communicate the design of a document. If you\u0026amp;rsquo;ve ever seen website copy like \u0026amp;ldquo;Lorem ipsum dolor sit amet\u0026amp;rdquo;, …","permalink":"/posts/custom-ipsum-for-real-world-testing/","summary":" Pastures which are subject to overflow should be avoided. All lambs born in April or May should be drenched about August or September following, to be certain of ridding them of worms that may later …","title":"Custom Ipsum for Real-World Testing"},{"content":" \u0026amp;ldquo;I\u0026amp;rsquo;m still getting this random 404.\u0026amp;rdquo;\nWant to sound pro? Banish \u0026amp;ldquo;random\u0026amp;rdquo; from your software engineering vocabulary.\nConsidering the Modern Usage \u0026amp;ldquo;Random\u0026amp;rdquo; has a modern usage; let\u0026amp;rsquo;s consider it. Take this example:\n\u0026amp;ldquo;I got a random bill from my dentist.\u0026amp;rdquo;\nWe hear: \u0026amp;ldquo;This surprising thing, a bill from my dentist, happened.\u0026amp;rdquo; We get it. It works. So what\u0026amp;rsquo;s my issue?\nMerriam-Webster defines \u0026amp;ldquo;random\u0026amp;rdquo; as \u0026amp;ldquo;lacking a definite plan, purpose, or pattern.\u0026amp;rdquo; And so, this usage is incorrect, because the bill …","permalink":"/posts/nothing-is-random/","summary":" \u0026ldquo;I\u0026rsquo;m still getting this random 404.\u0026rdquo;\nWant to sound pro? Banish \u0026ldquo;random\u0026rdquo; from your software engineering vocabulary.\n","title":"Banishing \"Random\" for Better Software Communication"},{"content":"Eight years ago I wrote a post called You Should Blog. In it, I made an argument that I still believe in: public, professional writing is something every engineer should try. Today, I want to share why I write, and have for over a decade.\nIt\u0026amp;rsquo;s Made Me a Better Programmer The first reason is that over time, I think it\u0026amp;rsquo;s made me a better programmer. To give you an example, here\u0026amp;rsquo;s an arbitrary opinion I hold, and how writing helped me solidify it: code coverage, the measurement of the percentage of your codebase that\u0026amp;rsquo;s covered by automated tests, is a poor measure of code …","permalink":"/posts/why-im-writing-to-you/","summary":"Eight years ago I wrote a post called You Should Blog. In it, I made an argument that I still believe in: public, professional writing is something every engineer should try. Today, I want to share …","title":"Why I Write for Software Engineers and Engineering Leaders"},{"content":"SALT messages are encoded with a library called msgpack, the Python implementation of MessagePack. How can we read them?\nHere\u0026amp;rsquo;s an example message I want to decode:\n£jid´20250602114126621578¦return£tcp§retcode\u0000¢idABC-LAPTOP-02£funªconfig.get¨fun_args©transport We can do that with the same library:\nimport msgpack with open(\u0026amp;#39;return.p\u0026amp;#39;, \u0026amp;#39;rb\u0026amp;#39;) as f: unp = msgpack.unpackb(f.read()) print(unp) Make sure you install msgpack:\npip install msgpack This will print something we can read (newlines added by me):\n{ \u0026amp;#39;jid\u0026amp;#39;: \u0026amp;#39;20250602114126621578\u0026amp;#39;, \u0026amp;#39;return\u0026amp;#39;: …","permalink":"/tils/unpacking-msgpack/","summary":"SALT messages are encoded with a library called msgpack, the Python implementation of MessagePack. How can we read them?\n","title":"Unpacking MessagePack"},{"content":"Today I learned about why directories in a project might have names ending in \u0026amp;lsquo;dot d\u0026amp;rsquo;, such as minion.d/.\nLike many things in CS, there seem to be a few answers that may have inspired each other, and can both be true. It\u0026amp;rsquo;s a marker for \u0026amp;lsquo;directory\u0026amp;rsquo;. It\u0026amp;rsquo;s a directory of configurations that will be merged on startup.\nIn my current experience, both have been true. SALT contains directories of configurations, that on startup are combined into the program state. So, I\u0026amp;rsquo;d expect that convention to hold in other such programs.\n","permalink":"/tils/directories-ending-in-dot-d/","summary":"Today I learned about why directories in a project might have names ending in \u0026lsquo;dot d\u0026rsquo;, such as minion.d/.\n","title":"Directories Ending in .d"},{"content":"I think a mark of a great engineer is being willing to change one\u0026amp;rsquo;s mind when presented with new information. Here are four technological things I\u0026amp;rsquo;ve changed my mind about.\nOwn Your Confusing Software Before: \u0026amp;ldquo;If a customer thinks software is confusing, they might just need training.\u0026amp;rdquo;\nToday: \u0026amp;ldquo;If a customer thinks your software is confusing, it is confusing.\u0026amp;rdquo;\nWhen building a SaaS product, it\u0026amp;rsquo;s easy to write off difficult or noisy customers. Especially if that\u0026amp;rsquo;s part of a team\u0026amp;rsquo;s culture.\nA more profitable approach is to own that your software …","permalink":"/posts/things-ive-changed-my-mind-about/","summary":"I think a mark of a great engineer is being willing to change one\u0026rsquo;s mind when presented with new information. Here are four technological things I\u0026rsquo;ve changed my mind about.\n","title":"Four Things I've Changed My Mind About in Engineering"},{"content":"Python string interpolation is available via few of interfaces, as Chris at Hashrocket taught me.\nToday I learned about formatted string literals, more commonly known as f-strings.\n\u0026amp;gt;\u0026amp;gt;\u0026amp;gt; animals = \u0026amp;#39;cats\u0026amp;#39; \u0026amp;gt;\u0026amp;gt;\u0026amp;gt; print(f\u0026amp;#39;My house is full of {animals}.\u0026amp;#39;) My house is full of cats. They\u0026amp;rsquo;re my preferred technique because they\u0026amp;rsquo;re similar to the interpolation syntax I\u0026amp;rsquo;ve come to know in JavaScript, Ruby, and Elixir.\nF-String Docs\n","permalink":"/tils/python-string-interpolation/","summary":"Python string interpolation is available via few of interfaces, as Chris at Hashrocket taught me.\n","title":"Python String Interpolation"},{"content":"I needed to replace an \u0026amp;ldquo;A19\u0026amp;rdquo; light bulb. What does \u0026amp;ldquo;A19\u0026amp;rdquo; mean?\nThe \u0026amp;ldquo;A\u0026amp;rdquo; describes the shape of the bulb; in this case, the standard household-shaped bulb (💡).\n\u0026amp;ldquo;19\u0026amp;rdquo; is more tricky. It\u0026amp;rsquo;s the diameter of the bulb in 1/8ths of an inch, so my bulb is 19 times 1/8th of an inch, or 2 3/8 inches. Countries on the metric system call these \u0026amp;ldquo;A60\u0026amp;rdquo; for 60 millimeters, which is\u0026amp;hellip; better!\nEverything is encoded! Signifiers like \u0026amp;ldquo;BR30\u0026amp;rdquo; (\u0026amp;ldquo;Bulged Reflector\u0026amp;rdquo;, 3.74inches x 8 ≈ \u0026amp;ldquo;30\u0026amp;rdquo;) are never random.\n","permalink":"/tils/light-bulb-series-encoding/","summary":"I needed to replace an \u0026ldquo;A19\u0026rdquo; light bulb. What does \u0026ldquo;A19\u0026rdquo; mean?\n","title":"Light Bulb Series Encoding"},{"content":"In Vim Normal mode, * searches forward for the next occurrence of word. But what goes back?\nIt\u0026amp;rsquo;s #!\nThis TIL illustrates how we get set in our ways with our tools. I found a way to use Vim for over a decade without knowing how to do this. Today, I learned.\nSee :help # for more details.\n","permalink":"/tils/vim-jump-to-previous-occurrence/","summary":"In Vim Normal mode, * searches forward for the next occurrence of word. But what goes back?\n","title":"Vim Jump to Previous Occurrence"},{"content":"If I\u0026amp;rsquo;ve defined a function in the Python REPL, I can read its definition with inspect.getsource.\n\u0026amp;gt;\u0026amp;gt;\u0026amp;gt; def greet(name): print(f\u0026amp;#34;Hello, {name}!\u0026amp;#34;) \u0026amp;gt;\u0026amp;gt;\u0026amp;gt; import inspect \u0026amp;gt;\u0026amp;gt;\u0026amp;gt; inspect.getsource(greet) \u0026amp;#39;def greet(name):\\n print(f\u0026amp;#34;Hello, {name}!\u0026amp;#34;)\\n\u0026amp;#39; And print it, too:\n\u0026amp;gt;\u0026amp;gt;\u0026amp;gt; print(inspect.getsource(greet)) def greet(name): print(f\u0026amp;#34;Hello, {name}!\u0026amp;#34;) ","permalink":"/tils/print-python-function-definition-with-getsource/","summary":"If I\u0026rsquo;ve defined a function in the Python REPL, I can read its definition with inspect.getsource.\n","title":"Print Python Function Definition With Getsource"},{"content":"In the Python REPL, the help function provides a help message about its argument.\n\u0026amp;gt;\u0026amp;gt;\u0026amp;gt; help(print) Help on built-in function print in module builtins: print(*args, sep=\u0026amp;#39; \u0026amp;#39;, end=\u0026amp;#39;\\n\u0026amp;#39;, file=None, flush=False) Prints the values to a stream, or to sys.stdout by default. It\u0026amp;rsquo;s a wrapper for pydoc.help. And it works on objects, too:\n\u0026amp;gt;\u0026amp;gt;\u0026amp;gt; list = [\u0026amp;#34;one\u0026amp;#34;, \u0026amp;#34;two\u0026amp;#34;] \u0026amp;gt;\u0026amp;gt;\u0026amp;gt; help(list) Help on list object: class list(object) | list(iterable=(), /) | | Built-in mutable sequence. Learn more by reading help\u0026amp;rsquo;s help:\n\u0026amp;gt;\u0026amp;gt;\u0026amp;gt; help(help) ","permalink":"/tils/python-repl-help/","summary":"In the Python REPL, the help function provides a help message about its argument.\n","title":"Python REPL Help"},{"content":"Receiving feature requests is part of running a software team. Here\u0026amp;rsquo;s how I handle them.\nWhat are Feature Requests? Feature requests are requests for behavior in a software product. Sometimes bugs get lumped in, too; it\u0026amp;rsquo;s often a little messy.\nFeature requests come from both inside and outside your company. Some are valuable, some aren\u0026amp;rsquo;t, others contradict things we\u0026amp;rsquo;ve already built, or may want to build. Some come from customers who are about to churn. Others might make sense for one customer but would confuse others.\nIdeas are cheap. Every feature is possible, but …","permalink":"/posts/how-i-approach-feature-requests/","summary":"Receiving feature requests is part of running a software team. Here\u0026rsquo;s how I handle them.\n","title":"How I Approach Feature Requests"},{"content":"Jira does have a \u0026amp;ldquo;Start Standup\u0026amp;rdquo; button, but it\u0026amp;rsquo;s hidden and not well documented.\nTo enter start a standup in Jira, open an active Kanban-style board.\nIf there\u0026amp;rsquo;s a \u0026amp;ldquo;Start Standup\u0026amp;rdquo; button in the upper right (this appears to be experimental), click it.\nYour board converts to a simplified standup presentation!\nOn the left, each team member is listed in random order, which you can shuffle. I like to mix up the order of reports every day, so this feature helps me do that. Teammates can also be skipped.\nYou can assign a timer for each member that restarts as you …","permalink":"/tils/jira-standup-mode/","summary":"Jira does have a \u0026ldquo;Start Standup\u0026rdquo; button, but it\u0026rsquo;s hidden and not well documented.\n","title":"How to Start a Standup in Jira"},{"content":"This is a response to \u0026amp;ldquo;Don\u0026amp;rsquo;t Guess\u0026amp;rdquo; from the excellent \u0026amp;ldquo;The Best Programmers I Know\u0026amp;rdquo; by Matthias Endler. My goal is to crystallize my understanding of this trait.\n\u0026amp;ldquo;In the face of ambiguity, refuse the temptation to guess.\u0026amp;rdquo; \u0026amp;ndash;The Zen of Python, Tim Peters\nGuessing is defined as \u0026amp;ldquo;form[ing] an opinion of from little or no evidence.\u0026amp;rdquo; As a programmer, it\u0026amp;rsquo;s tempting to guess while debugging. We might say: \u0026amp;ldquo;Hey, I wonder if this is happening in GamePlanSyncer? That sure causes a lot of bugs\u0026amp;rdquo;, while nowhere near the …","permalink":"/posts/dont-guess/","summary":"This is a response to \u0026ldquo;Don\u0026rsquo;t Guess\u0026rdquo; from the excellent \u0026ldquo;The Best Programmers I Know\u0026rdquo; by Matthias Endler. My goal is to crystallize my understanding of this trait.\n","title":"Don't Guess"},{"content":"If something in your software is confusing to a customer, it is confusing.\nIt is! There is something about it, that you might not see, that doesn\u0026amp;rsquo;t make sense to a nonzero number of your customers. Stop trying to outsmart the bug report and consider it.\nWill you encounter a few outlier customers who are hard to please? A few more who struggle with what seem like obvious interfaces? Yes and yes.\nMost customers, however, are people of average patience and competency who just want the thing to work. They\u0026amp;rsquo;d prefer not to ask for help. They tried to solve the problem themselves.\nMy …","permalink":"/posts/your-software-is-confusing/","summary":"If something in your software is confusing to a customer, it is confusing.\n","title":"Your Software Is Confusing"},{"content":"I\u0026amp;rsquo;ve added TypeScript to several projects I\u0026amp;rsquo;ve worked on. In this post, I\u0026amp;rsquo;d like to discuss why I think TypeScript is essential and document my expectations around it.\nWhat Is TypeScript? TypeScript is a type-safe superset of JavaScript developed by Microsoft. JavaScript is a dynamic language and doesn\u0026amp;rsquo;t have type-safety. But with TypeScript, it does!\nIt\u0026amp;rsquo;s also a gradual type system, so one file in a codebase can be converted from .js to .ts, and with a server or process restart, type-checking is performed on that file.\nIn React apps, it runs on your development …","permalink":"/posts/why-im-all-in-on-typescript/","summary":"I\u0026rsquo;ve added TypeScript to several projects I\u0026rsquo;ve worked on. In this post, I\u0026rsquo;d like to discuss why I think TypeScript is essential and document my expectations around it.\n","title":"Why I'm \"All In\" on TypeScript"},{"content":"I have a cron job that opens a program every day at a certain time. How can I also close it with a cron job?\nTo open:\n45 9 * * 1-5 open -a \u0026amp;#34;SomeProgram\u0026amp;#34; And to close a few minutes later:\n50 9 * * 1-5 killall \u0026amp;#34;SomeProgram\u0026amp;#34; See man killall for more information.\n","permalink":"/tils/kill-macos-processes-by-name/","summary":"I have a cron job that opens a program every day at a certain time. How can I also close it with a cron job?\n","title":"Kill MacOS Processes by Name"},{"content":"My friend Josh recently wrote about a common mistake using pbcopy, Apple\u0026amp;rsquo;s pasteboard utility.\nTo paraphrase, the bad version creates a file called pbcopy:\n$ cat .envrc › pbcopy While the good version sends the file to your clipboard:\ncat .envrc | pbcopy There\u0026amp;rsquo;s a third approach that avoids cat entirely, and may sidestep this mistake for some:\npbcopy \u0026amp;lt; .envrc ","permalink":"/tils/pbcopy-no-cat/","summary":"My friend Josh recently wrote about a common mistake using pbcopy, Apple\u0026rsquo;s pasteboard utility.\n","title":"pbcopy, no cat"},{"content":"Anytime Chrome loads a webpage, you can pause script execution without a debugger.\nOpen the Sources tab and then click pause. Or, if you prefer to be hands-on-keyboard, type CMD + \\ or F8.\nThis aids debugging, and I find it interesting to see all the scripts that run on a production website, even after it appears that loading is complete.\n","permalink":"/tils/pause-script-execution-in-chrome/","summary":"Anytime Chrome loads a webpage, you can pause script execution without a debugger.\n","title":"Pause Script Execution in Chrome"},{"content":"I recently learned a practice for customer communication that I\u0026amp;rsquo;d like to document. It\u0026amp;rsquo;s called \u0026amp;ldquo;Hit It Twice.\u0026amp;rdquo;\nThe quoted language in here is broad and should be adapted to the situation.\nWhy This Matters Customer issues deserve prompt attention. And, we have to prioritize customer issues that matter. Customers responding to offers of help, or not, tells us how much they care about the issue.\n\u0026amp;ldquo;Hit It Twice\u0026amp;rdquo; Scenario: a customer opens a support ticket. We respond with a proposed solution or a clarifying question, ensuring they have the next step to move …","permalink":"/posts/hit-it-twice/","summary":"I recently learned a practice for customer communication that I\u0026rsquo;d like to document. It\u0026rsquo;s called \u0026ldquo;Hit It Twice.\u0026rdquo;\n","title":"Two-Follow-Up Rule for Closing Support and Internal Tickets"},{"content":"One common trait among early-career programmers is seeing technology choices in black-and-white. I\u0026amp;rsquo;ve been there. \u0026amp;ldquo;Redux is awesome!\u0026amp;rdquo; \u0026amp;ldquo;Nested ternaries are terrible!\u0026amp;rdquo; As you advance as a programmer, for better or worse, you start to see almost everything as a trade-off.\nIt\u0026amp;rsquo;s Tradeoffs All the Way Down Trade-offs at a low level, such as choices made within an individual code file, and trade-offs at a strategic level, such as which technology tools to invest in and people to hire. It often feels like it\u0026amp;rsquo;s trade-offs all the way down.\nAt the low level, in …","permalink":"/posts/its-tradeoffs-all-the-way-down/","summary":"One common trait among early-career programmers is seeing technology choices in black-and-white. I\u0026rsquo;ve been there. \u0026ldquo;Redux is awesome!\u0026rdquo; \u0026ldquo;Nested ternaries are terrible!\u0026rdquo; As …","title":"It's Tradeoffs All the Way Down"},{"content":"My definition of \u0026amp;ldquo;I don\u0026amp;rsquo;t understand\u0026amp;rdquo; debugging.\nI once heard Malcolm Gladwell claim that his father, a professor, would have never fallen for Bernie Madoff\u0026amp;rsquo;s Ponzi scheme. He felt his father was too curious and unafraid to admit when he didn\u0026amp;rsquo;t understand something. He\u0026amp;rsquo;d listen to the pitch, then quietly say: \u0026amp;ldquo;I don\u0026amp;rsquo;t understand that; please explain it to me again.\u0026amp;rdquo; Again and again, until the explainer gave up.\nI call this \u0026amp;ldquo;I don\u0026amp;rsquo;t understand\u0026amp;rdquo; debugging. It\u0026amp;rsquo;s something I\u0026amp;rsquo;m proud to have picked up watching some …","permalink":"/posts/i-dont-understand-debugging/","summary":"My definition of \u0026ldquo;I don\u0026rsquo;t understand\u0026rdquo; debugging.\n","title":"\"I Don't Understand\" Debugging"},{"content":"I\u0026amp;rsquo;ve been organizing Meetups for a decade, starting with Vim Chicago and Chicago Elixir, and now running Maine JS from Portland, Maine. In honor of our most recent Meetup, here\u0026amp;rsquo;s my practical guide on how to start a Meetup group, based on what\u0026amp;rsquo;s worked for me.\nHow to Start a Meetup Group The steps that follow are roughly in order. You\u0026amp;rsquo;re going to be doing many of them in the beginning; that\u0026amp;rsquo;s the time when you do the most work.\nI\u0026amp;rsquo;m not going to cover signing up for Meetup and filling web forms, but rather, how you should think about getting started. …","permalink":"/posts/how-i-organize-a-meetup/","summary":"I\u0026rsquo;ve been organizing Meetups for a decade, starting with Vim Chicago and Chicago Elixir, and now running Maine JS from Portland, Maine. In honor of our most recent Meetup, here\u0026rsquo;s my …","title":"How to Start a Meetup Group (Lessons from Running One for 10 Years)"},{"content":"Here\u0026amp;rsquo;s my annual review covering 2024.\nWork Big news: the software portal I\u0026amp;rsquo;d been building for over a year launched! It was a ton of work: ups, downs, and updating caniuse-lite. We defied escape velocity and launched. I was proud of my team and my growth in achieving this milestone.\nSoon afterward, I got a new job and got promoted! I\u0026amp;rsquo;m enjoying the challenge. I\u0026amp;rsquo;ve learned that I\u0026amp;rsquo;m happiest when being pushed and working on tough problems. I\u0026amp;rsquo;m grateful for James Stanier\u0026amp;rsquo;s book Become an Effective Software Engineering Manager, which I bookmarked …","permalink":"/posts/my-annual-review-2024/","summary":"Here\u0026rsquo;s my annual review covering 2024.\n","title":"My Annual Review 2024"},{"content":"This post is about how to write software well. It was inspired by this post with a similar title. Read it and write yours.\nThese ideas aren\u0026amp;rsquo;t advice but rather notes about how I do my best work. If you\u0026amp;rsquo;re new, some of these ideas might seem obvious, but they get harder as you progress, or your understanding of them deepens, or you\u0026amp;rsquo;ll encounter engineers who embody them in surprising ways.\nThe Habits Keep getting faster. You can\u0026amp;rsquo;t change the world if you\u0026amp;rsquo;re held back by your typing ability, or fighting your editor, terminal tools, browser, ticketing and design …","permalink":"/posts/hard-won-software-development-habits/","summary":"This post is about how to write software well. It was inspired by this post with a similar title. Read it and write yours.\n","title":"Hard-Won Software Development Habits"},{"content":"TONY: We should take this outside.\nJARVIS (O.S.): I must strongly caution against that. There are terabytes of calculations still needed –\nTONY: We\u0026amp;rsquo;ll do them in-flight.\nBefore Your Take it Outside (Ship) There\u0026amp;rsquo;s been a lot written in our industry about the value of taking it outside (shipping). Today I\u0026amp;rsquo;d like to write a little more, with an idea that I think makes the value of shipping clear: shipping tells you, precisely, what matters.\nBefore you ship, everything is theory. Does the landing page defaulting to sign up versus sign in increase user acquisition? Nobody is …","permalink":"/posts/we-should-take-this-outside/","summary":"TONY: We should take this outside.\n","title":"Ship Earlier to Learn What Matters"},{"content":"I was the third engineer hired by my company, not counting our technical co-founder. I like that position, and it seems to play to my strengths.\nWho Is the Third Engineer? Who is the third engineer? Looking at the startup hiring process, I\u0026amp;rsquo;ve noticed a pattern. The first engineer is usually a trusted senior that the founders know. The second engineer is the same, or a referral from the first engineer.\nAfter that, we\u0026amp;rsquo;re running out of known quantities. That third engineer may be the first conventional outside hire. Being that person presents unique challenges.\nFirst, because …","permalink":"/posts/being-the-third-engineer/","summary":"I was the third engineer hired by my company, not counting our technical co-founder. I like that position, and it seems to play to my strengths.\n","title":"Being the Third Engineer"},{"content":"Today I\u0026amp;rsquo;d like to talk about a quality that\u0026amp;rsquo;s essential to success as a computer programmer. Let\u0026amp;rsquo;s call it \u0026amp;ldquo;comfort with discomfort.\u0026amp;rdquo; As programmers, we live in this unsettling space. Here are some thoughts on discomfort and tips for dealing with it skillfully.\nExample: Grow Cube When I first started programming, I took a computer science course that presented a puzzle called the \u0026amp;ldquo;Grow Cube.\u0026amp;rdquo; You can play it online here:\nGrow cube\nThis puzzle starts with a 3D cube world and some buttons, and pressing those buttons initiates actions on the cube: water …","permalink":"/posts/comfort-with-discomfort/","summary":"Today I\u0026rsquo;d like to talk about a quality that\u0026rsquo;s essential to success as a computer programmer. Let\u0026rsquo;s call it \u0026ldquo;comfort with discomfort.\u0026rdquo; As programmers, we live in this …","title":"Getting Comfortable With Discomfort in Software Engineering"},{"content":"Often, when you work as an engineer on a small team, you don\u0026amp;rsquo;t have dedicated designers on staff. How can you deliver beautiful, intuitive software without designers? Here\u0026amp;rsquo;s a trick that helps me: decoupling my design work from my engineering work.\nClarification: I Love Designers! Let me start by saying: I love designers! An expert designer is one of the most vital people in an engineering organization. I\u0026amp;rsquo;m not a designer. Great designers are rare, which is why, for much of my career, I haven\u0026amp;rsquo;t had one.\nThis post is for the rest of us: software engineers who don\u0026amp;rsquo;t …","permalink":"/posts/decoupling-design-from-engineering/","summary":"Often, when you work as an engineer on a small team, you don\u0026rsquo;t have dedicated designers on staff. How can you deliver beautiful, intuitive software without designers? Here\u0026rsquo;s a trick that …","title":"Decoupling Design From Engineering"},{"content":"Our industry has a concept of \u0026amp;ldquo;10x engineers\u0026amp;rdquo;, individual contributors who have the impact of ten colleagues. How can you become one? I\u0026amp;rsquo;ll try to answer that in this post.\nBut first, here\u0026amp;rsquo;s the price of admission: you first have to be both fast and competent. And that\u0026amp;rsquo;s not enough.\nWhat\u0026amp;rsquo;s fast? Reducing waste in every part of your development process. Cutting to the bone the time it takes to type, save and share your work, communicate, and hack your way to something good enough to ship. \u0026amp;ldquo;Fast\u0026amp;rdquo; and \u0026amp;ldquo;great\u0026amp;rdquo; are tightly correlated in …","permalink":"/posts/the-secret-to-being-a-10x-engineer/","summary":"Our industry has a concept of \u0026ldquo;10x engineers\u0026rdquo;, individual contributors who have the impact of ten colleagues. How can you become one? I\u0026rsquo;ll try to answer that in this post.\n","title":"The Secret to Being a 10x Engineer"},{"content":"Sometimes in TypeScript we\u0026amp;rsquo;d like to say a function can either have one typed prop, or the other, never both and never neither. This can be achieved with a union type and type never:\ntype Props = { markdown: string, copy?: never } | { markdown?: never, copy: string} }; const component = ({ markdown, copy }: Props) =\u0026amp;gt; markdown ? parseMarkdown(markdown) : \u0026amp;lt;\u0026amp;gt;copy\u0026amp;lt;/\u0026amp;gt;; console.log(component({ markdown: \u0026amp;#34;### Important\u0026amp;#34; })) // \u0026amp;lt;h3\u0026amp;gt;Important\u0026amp;lt;/h3\u0026amp;gt; console.log(component({ copy: \u0026amp;#34;Just information\u0026amp;#34; })) // \u0026amp;lt;\u0026amp;gt;Just information\u0026amp;lt;/\u0026amp;gt; …","permalink":"/tils/either-or-props-in-typescript/","summary":"Sometimes in TypeScript we\u0026rsquo;d like to say a function can either have one typed prop, or the other, never both and never neither. This can be achieved with a union type and type never:\ntype Props …","title":"Either/Or Props in TypeScript"},{"content":"If you\u0026amp;rsquo;re creating a hyperlink with query params, and those param values can have plus signs, it\u0026amp;rsquo;s important to encode them values. We do this with encodeURIComponent:\nconst email = `jake+testing@example.com` const loginLink = `https://example.com/login?email=${encodeURIComponent(email)}` window.open(loginLink) If you skip this step and try to read the query parameters received by \u0026amp;rsquo;example.com\u0026amp;rsquo; your email param value may be interpreted as with spaces instead of pluses, i.e. jake testing@example.com. Per the docs:\nWithin the query string, the plus sign is reserved as …","permalink":"/tils/encoding-plus-signs-in-urls/","summary":"If you\u0026rsquo;re creating a hyperlink with query params, and those param values can have plus signs, it\u0026rsquo;s important to encode them values. We do this with encodeURIComponent:\nconst email = …","title":"Encoding Plus Signs in URLS"},{"content":"Exploring your JS dependencies locally is a great way to learn and experiment. Here\u0026amp;rsquo;s how to load a dependency from your /node_modules directory into the Node REPL.\n$ node \u0026amp;gt; cn = require(\u0026amp;#39;classnames\u0026amp;#39;) \u0026amp;gt; cn(\u0026amp;#34;always\u0026amp;#34;, { never: false, sometimes: true }) \u0026amp;#39;always sometimes\u0026amp;#39; ","permalink":"/tils/load-a-dependency-in-node-repl/","summary":"Exploring your JS dependencies locally is a great way to learn and experiment. Here\u0026rsquo;s how to load a dependency from your /node_modules directory into the Node REPL.\n$ node \u0026gt; cn = …","title":"Load a Dependency in the Node REPL"},{"content":"I\u0026amp;rsquo;m reading Functional-Light JavaScript by Kyle Simpson, and learning a lot! Today I learned about the functional programming utility known as \u0026amp;lsquo;identity\u0026amp;rsquo;.\nIdentity is a unary function that simply returns its argument. A simple idea that can be powerfully applied, as JavaScript coerces the returned argument to boolean:\n\u0026amp;gt; const identity = (arg) =\u0026amp;gt; arg \u0026amp;gt; [\u0026amp;#34;\u0026amp;#34;, false, \u0026amp;#34;keep\u0026amp;#34;, null, undefined, \u0026amp;#34;these\u0026amp;#34;].filter(identity) [ \u0026amp;#39;keep\u0026amp;#39;, \u0026amp;#39;these\u0026amp;#39; ] I\u0026amp;rsquo;ve done something similar for years by filtering to boolean, or writing my own (I …","permalink":"/tils/filtering-by-identity/","summary":"I\u0026rsquo;m reading Functional-Light JavaScript by Kyle Simpson, and learning a lot! Today I learned about the functional programming utility known as \u0026lsquo;identity\u0026rsquo;.\nIdentity is a unary …","title":"Filtering By Identity"},{"content":"If you\u0026amp;rsquo;ve ever watched me debug, you might think I\u0026amp;rsquo;m moving slowly. That\u0026amp;rsquo;s because I try hard to find every marker on the debugging trail. I believe this is one of the most valuable skills in debugging.\nWhat\u0026amp;rsquo;s a Marker? Let\u0026amp;rsquo;s say our debugging session is a trail through the forest. \u0026amp;lsquo;O\u0026amp;rsquo; is where we are, and \u0026amp;lsquo;X\u0026amp;rsquo; is the summit of a mountain. At \u0026amp;lsquo;O\u0026amp;rsquo;, we know little. At \u0026amp;lsquo;X\u0026amp;rsquo;, we understand the bug and we\u0026amp;rsquo;ve fixed it.\nO------------------------X The path is marked by spray-painted swatches on the trees. These are …","permalink":"/posts/dont-skip-debugging-steps/","summary":"If you\u0026rsquo;ve ever watched me debug, you might think I\u0026rsquo;m moving slowly. That\u0026rsquo;s because I try hard to find every marker on the debugging trail. I believe this is one of the most valuable …","title":"Find Every Debugging Trail Marker"},{"content":"Reviewing code is tricky. When I\u0026amp;rsquo;m doing it, I\u0026amp;rsquo;m trying to achieve a few things at once. In this post, I\u0026amp;rsquo;d like to document the ways I try to add value via code reviews.\nMy Background \u0026amp;amp; Biases This is the second post I\u0026amp;rsquo;ve written on this subject; here\u0026amp;rsquo;s the first.\nSince 2022, my approach has shifted from strategic to tactical; I don\u0026amp;rsquo;t prioritize design (the high-level programmatic approach) as much, which I\u0026amp;rsquo;ll explain in a moment.\nWhen I started programming, I found myself on a team of very experienced engineers. Some of them did not hesitate to …","permalink":"/posts/how-i-review-code-pt-2/","summary":"Reviewing code is tricky. When I\u0026rsquo;m doing it, I\u0026rsquo;m trying to achieve a few things at once. In this post, I\u0026rsquo;d like to document the ways I try to add value via code reviews.\n","title":"How I Review Code, Part 2"},{"content":"Today I solved a mystery: a file was being Git-ignored in a new project that didn\u0026amp;rsquo;t have a .gitignore. Here\u0026amp;rsquo;s me learning this by trying and failing to add it:\n$ git add destroy.sh The following paths are ignored by one of your .gitignore files: destroy.sh This output tells me some .gitignore is telling Git to ignore my script. Another way to confirm this is the check-ignore command:\n$ git check-ignore destroy.sh destroy.sh The output here is the match; a file named destroy.sh is indeed being ignored. But how? We can answer that question with the -v flag:\n$ git check-ignore …","permalink":"/tils/show-how-a-file-is-ignored-in-git/","summary":"Today I solved a mystery: a file was being Git-ignored in a new project that didn\u0026rsquo;t have a .gitignore. Here\u0026rsquo;s me learning this by trying and failing to add it:\n$ git add destroy.sh The …","title":"Show How A File Is Ignored In Git"},{"content":"I\u0026amp;rsquo;m using a JavaScript text editor library and when it fails to load, there is no form field on the page at all. While pondering that potential issue, I learned how to block a URL in Chrome\u0026amp;rsquo;s DevTools.\nThere are a few way to do this; the most direct for me is:\nOpen DevTools and the Network Tab Click \u0026amp;ldquo;JS\u0026amp;rdquo; and find the JavaScript file I want to block Right-click and choose \u0026amp;ldquo;Block request URL\u0026amp;rdquo; or \u0026amp;ldquo;Block request domain\u0026amp;rdquo; Reload the page to see the result.\n","permalink":"/tils/block-a-url-in-chrome-devtools/","summary":"I\u0026rsquo;m using a JavaScript text editor library and when it fails to load, there is no form field on the page at all. While pondering that potential issue, I learned how to block a URL in …","title":"Block a URL in Chrome DevTools"},{"content":"Complex HTML forms often end up with multiple submit buttons. Consider a form for taxes: there\u0026amp;rsquo;s a button to save a draft and another button to submit for processing. They both trigger the submit action on the same form. How could we accomplish this while keeping the buttons fairly simple?\nOne solution is to set the buttons\u0026amp;rsquo; value:\n\u0026amp;lt;form onsubmit={onSubmit}\u0026amp;gt; \u0026amp;lt;!-- Form fields here --\u0026amp;gt; \u0026amp;lt;input type=\u0026amp;#34;submit\u0026amp;#34; value=\u0026amp;#34;save\u0026amp;#34; /\u0026amp;gt; \u0026amp;lt;input type=\u0026amp;#34;submit\u0026amp;#34; value=\u0026amp;#34;submit\u0026amp;#34; /\u0026amp;gt; \u0026amp;lt;/form\u0026amp;gt; Then in the submit handler, we read the value.\nconst …","permalink":"/tils/reading-a-buttons-value-on-submit/","summary":"Complex HTML forms often end up with multiple submit buttons. Consider a form for taxes: there\u0026rsquo;s a button to save a draft and another button to submit for processing. They both trigger the …","title":"Reading a Button's Value on Submit"},{"content":"In Zsh, if you type man cd, instead of a manual page, you\u0026amp;rsquo;ll get this:\nSee the built-in command description in the appropriate shell manual page. To see Zsh\u0026amp;rsquo;s implementation of cd, we need to view Zsh\u0026amp;rsquo;s built-in man page:\nman zshbuiltins What\u0026amp;rsquo;s going on here? Here\u0026amp;rsquo;s a summary:\nBuiltin commands are contained within the shell itself. When the name of a builtin command is used as the first word of a simple command (see Simple Commands), the shell executes the command directly, without invoking another program. Builtin commands are necessary to implement functionality …","permalink":"/tils/read-zsh-builtin-manual-pages/","summary":"In Zsh, if you type man cd, instead of a manual page, you\u0026rsquo;ll get this:\nSee the built-in command description in the appropriate shell manual page. To see Zsh\u0026rsquo;s implementation of cd, we need …","title":"Read Zsh Builtin Manual Pages"},{"content":"autofocus is a global attribute that sets a field as focused on page load.\n\u0026amp;lt;input name=\u0026amp;#34;first_name\u0026amp;#34; autofocus /\u0026amp;gt; No JavaScript required!\nA use case could be a new customer form that an employee fills out multiple times a day. By autofocusing the first name field, you could save that employee thousands of clicks.\nCarefully consider user experience and accessibility before using this attribute.\nDocs: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus\n","permalink":"/tils/autofocus-a-form-field/","summary":"autofocus is a global attribute that sets a field as focused on page load.\n\u0026lt;input name=\u0026#34;first_name\u0026#34; autofocus /\u0026gt; No JavaScript required!\nA use case could be a new customer form that an …","title":"Autofocus a Form Field"},{"content":"When using radio, you might experience a strange phenomenon: the radio clock refuses to stay correctly set. I saw this recently on a car stereo that continued to reset its clock to an hour early for my timezone.\nThe culprit is a system called RDS, Radio Data System. RDS allows radio transmitters to encode their program information, station identification, and\u0026amp;hellip; the local time.\nThough I didn\u0026amp;rsquo;t have a tool to debug the signal on hand, my theory is that a local radio station was sending the incorrect time of 10:15 AM instead 11:15 AM. My car was read it and preferred this data. We …","permalink":"/tils/rds-breaks-my-clock/","summary":"When using radio, you might experience a strange phenomenon: the radio clock refuses to stay correctly set. I saw this recently on a car stereo that continued to reset its clock to an hour early for …","title":"RDS Breaks My Radio Clock"},{"content":"When doing web programming in a context where scroll might be expected, you may see some surprising behavior in the scrollable HTML– an inexplicable gray bar on the bottom or right of the element.\nOne culprit, on Mac, is the user\u0026amp;rsquo;s scrollbar setting.\nAs a UX and accessibility aid, Mac allows users to always show scrollbars when they are available. Considering this feature as a programmer, you can make sure you only offer scrolling when it makes sense, and educate users who don\u0026amp;rsquo;t know they have this setting enabled.\nSee \u0026amp;ldquo;Settings \u0026amp;gt; Appearance \u0026amp;gt; Show scroll bars\u0026amp;rdquo; to …","permalink":"/tils/understanding-scrollbar-visiblity/","summary":"When doing web programming in a context where scroll might be expected, you may see some surprising behavior in the scrollable HTML– an inexplicable gray bar on the bottom or right of the element.\n","title":"Understanding Scrollbar Visibility"},{"content":"Today I had a situation where I wanted a child component to re-render on command. The solution I found was to to put a key on it, and then change the key when the re-render is desired.\nconst Parent = () =\u0026amp;gt; { const [childKey, setChildKey] = useState(0); const rerenderForm = () =\u0026amp;gt; { setChildKey(childKey + 1); } return ( \u0026amp;lt;\u0026amp;gt; \u0026amp;lt;button onClick={rerenderForm} type=\u0026amp;#34;button\u0026amp;#34;\u0026amp;gt;Rerender the form!\u0026amp;lt;/button\u0026amp;gt; \u0026amp;lt;Form key={childKey} /\u0026amp;gt; \u0026amp;lt;/\u0026amp;gt; ) } In this example, which the button is clicked, our childKey is incremented. To paraphrase the React docs, when the key changes, …","permalink":"/tils/rerender-child-component-by-changing-key/","summary":"Today I had a situation where I wanted a child component to re-render on command. The solution I found was to to put a key on it, and then change the key when the re-render is desired.\nconst Parent = …","title":"Re-render Child Component By Changing Key"},{"content":"Be careful when making a network request like this to a Rails controller with strong parameters:\n// const employee = { name: \u0026amp;#34;Jake\u0026amp;#34; }; const updateEmployee = (id: number, employee: Employee) =\u0026amp;gt; axios.put\u0026amp;lt;Employee\u0026amp;gt;(`/employees/${id}`, employee) def employee_params params.require(:employee).permit(:name) end Note that our payload is expected under key employee– but we aren\u0026amp;rsquo;t sending it that way. When this happens, Rails infers what goes where:\nPUT https://server.dev/employees/1 { \u0026amp;#34;employee\u0026amp;#34; =\u0026amp;gt; { \u0026amp;#34;name\u0026amp;#34; =\u0026amp;gt; \u0026amp;#34;Jake\u0026amp;#34; } } Notice that name is in the …","permalink":"/tils/controller-parameters-infered-from-the-model/","summary":"Be careful when making a network request like this to a Rails controller with strong parameters:\n// const employee = { name: \u0026#34;Jake\u0026#34; }; const updateEmployee = (id: number, employee: Employee) …","title":"Inferred Controller Parameters"},{"content":"When testing a serialized Rails API with RSpec, a common mistake is to assert about the response body, only to find that it\u0026amp;rsquo;s an empty string. The controller is controlling, the view is presenting. What\u0026amp;rsquo;s missing?\nWhat\u0026amp;rsquo;s missing is the view: RSpec views are stubbed by default. This is ideal for most controller tests. Just not those asserting about serialized data.\nRender those views with render_views:\nrequire \u0026amp;#34;rails_helper\u0026amp;#34; RSpec.describe WidgetsController, type: :controller do render_views describe \u0026amp;#34;GET index\u0026amp;#34; do it \u0026amp;#34;has a widgets related heading\u0026amp;#34; do …","permalink":"/tils/assert-about-response-body-in-rspec-controller-tests/","summary":"When testing a serialized Rails API with RSpec, a common mistake is to assert about the response body, only to find that it\u0026rsquo;s an empty string. The controller is controlling, the view is …","title":"Assert About Response Body in RSpec Controller Tests"},{"content":"Passing props conditionally a component makes sense in many situations. Imagine a select box that receives an isDisabled prop that disables the input. Maybe we only want to set it if it\u0026amp;rsquo;s true. For one, it\u0026amp;rsquo;s a bit cleaner than setting it to its default false much of the time. And maybe we want to select a certain default value in that same scenario.\nEnough theorizing! Here\u0026amp;rsquo;s the code:\n\u0026amp;lt;Select options={itemOptions} {...(someCondition \u0026amp;amp;\u0026amp;amp; { isDisabled: true, defaultValue: [itemOptions[0]] })} /\u0026amp;gt; Let me explain the magic.\nWhen our our boolean condition someCondition …","permalink":"/tils/conditional-props/","summary":"Passing props conditionally a component makes sense in many situations. Imagine a select box that receives an isDisabled prop that disables the input. Maybe we only want to set it if it\u0026rsquo;s true. …","title":"Conditional Props"},{"content":"Want to connect a list of React components with a delimiter, like a comma? Imagine a sentence of:\nLink A, Link B, Link C.\nHow could we programmatically achieve this in React?\nHere\u0026amp;rsquo;s one answer, inserting a comma before all but the first list item:\n{items.map((item, index) =\u0026amp;gt; ( \u0026amp;lt;React.Fragment key={item.id}\u0026amp;gt; {index \u0026amp;gt; 0 \u0026amp;amp;\u0026amp;amp; \u0026amp;#39;, \u0026amp;#39;} \u0026amp;lt;a href={`/items/${item.id}`}\u0026amp;gt;{item.name}\u0026amp;lt;/a\u0026amp;gt; \u0026amp;lt;/React.Fragment\u0026amp;gt; ))} Lots of other good suggestions in this Gist.\n","permalink":"/tils/join-react-components-with-comma/","summary":"Want to connect a list of React components with a delimiter, like a comma? Imagine a sentence of:\nLink A, Link B, Link C.\nHow could we programmatically achieve this in React?\nHere\u0026rsquo;s one answer, …","title":"Join React Components With Comma"},{"content":"Suppose you want to append to a file that\u0026amp;rsquo;s write-protected and can only be edited via sudo. Is that possible? It is!\nA technique I discovered today is to use tee with sudo:\n$ cat file.txt | sudo tee -a /etc/hosts This lets me append the contents of file.txt to the write-protected /etc/hosts file by entering my password.\n","permalink":"/tils/append-to-a-write-protected-file/","summary":"Suppose you want to append to a file that\u0026rsquo;s write-protected and can only be edited via sudo. Is that possible? It is!\nA technique I discovered today is to use tee with sudo:\n$ cat file.txt | …","title":"Append to a Write-Protected File"},{"content":"Reviewing a GitHub PR on that contains a lot of whitespace changes? Extra whitespace can make it challenging to evaluate the substance of a PR. It\u0026amp;rsquo;s high on my list of things that should generally not be refactored. Ignore whitespace by adding the query parameter ?w=1 to your diff URL.\n","permalink":"/tils/ignore-whitespace-in-a-github-pull-request/","summary":"Reviewing a GitHub PR on that contains a lot of whitespace changes? Extra whitespace can make it challenging to evaluate the substance of a PR. It\u0026rsquo;s high on my list of things that should …","title":"Ignore Whitespace in a GitHub Pull Request"},{"content":"Anytime you add a header to a markdown file, GitHub attaches an href with its downcased name. \u0026amp;lsquo;JavaScript\u0026amp;rsquo; receives a link to #javascript, for instance.\nLeverage this by adding a table of contents to your README with links to the headers further down the document:\n### Table of Contents - [Cucumber](#cucumber) This will link to a header containing \u0026amp;lsquo;Cucumber\u0026amp;rsquo;.\nh/t Josh Branchaud\n","permalink":"/tils/link-to-headers-in-github-readmes/","summary":"Anytime you add a header to a markdown file, GitHub attaches an href with its downcased name. \u0026lsquo;JavaScript\u0026rsquo; receives a link to #javascript, for instance.\nLeverage this by adding a table of …","title":"Link to Headers in GitHub READMEs"},{"content":"The Chrome DevTools Network Tab is incredibly useful for frontend web development, but it can be noisy.\nPerhaps you\u0026amp;rsquo;d like to filter it down by HTTP status code? See only redirects, so you can fix routes? Or only successful requests, so you can know what\u0026amp;rsquo;s working?\nThe Network Tab supports this. Here\u0026amp;rsquo;s a filter to just temporary redirects:\nstatus-code:307 ","permalink":"/tils/filter-network-requests-by-status-code-in-devtools/","summary":"The Chrome DevTools Network Tab is incredibly useful for frontend web development, but it can be noisy.\nPerhaps you\u0026rsquo;d like to filter it down by HTTP status code? See only redirects, so you can …","title":"Filter Network Requests By Status Code in DevTools"},{"content":"I recently calculated that I merge 0.8 pull requests every day into my team repo. Let\u0026amp;rsquo;s round up and say I merge about one PR every day, delivering one or more features to production. I like this velocity, and in this post, I\u0026amp;rsquo;ll explain how you can achieve it yourself.\nMy ideal day as an IC goes like this:\nMorning: start a ticket Early morning/early afternoon: open a PR Early afternoon: respond to comments and merge PR Afternoon: deliver work to a QA environment Late afternoon: deploy to production This doesn\u0026amp;rsquo;t happen by accident. It\u0026amp;rsquo;s the result of processes— mine and …","permalink":"/posts/how-to-deliver-code-every-day/","summary":"I recently calculated that I merge 0.8 pull requests every day into my team repo. Let\u0026rsquo;s round up and say I merge about one PR every day, delivering one or more features to production. I like …","title":"How to Deliver Code Every Day"},{"content":"⌘ + R.\n","permalink":"/tils/xcode-run-hotkey/","summary":"⌘ + R.\n","title":"XCode Run Hotkey"},{"content":"I\u0026amp;rsquo;ve been a practitioner of Shawn Wang\u0026amp;rsquo;s \u0026amp;lsquo;Learn in Public\u0026amp;rsquo; for years. In this post, I\u0026amp;rsquo;ll share a list of ways I\u0026amp;rsquo;ve found to learn in public.\nI think that learning in public can start with you, then your team, then the world. And so, these are ordered from small and private to big and public.\nTalk through a tough problem out loud. Instead of holding a difficult problem in your head, try building a solution out loud from a series of facts. \u0026amp;ldquo;We\u0026amp;rsquo;re seeing a bug on staging. It affects this page\u0026amp;hellip;\u0026amp;rdquo;. Talk to a duck if nobody is around.\nUse a …","permalink":"/posts/practical-ways-to-learn-in-public-now/","summary":"I\u0026rsquo;ve been a practitioner of Shawn Wang\u0026rsquo;s \u0026lsquo;Learn in Public\u0026rsquo; for years. In this post, I\u0026rsquo;ll share a list of ways I\u0026rsquo;ve found to learn in public.\n","title":"Practical Ways to 'Learn in Public' Now"},{"content":"I recently hit a decade of professional programming. I\u0026amp;rsquo;d like to take a moment here and reflect on what I\u0026amp;rsquo;ve learned.\nThis post is for me ten years ago. These ideas are always evolving. Maybe they\u0026amp;rsquo;ll reinforce something you heard somewhere. Maybe you\u0026amp;rsquo;ll agree, or disagree. I\u0026amp;rsquo;d be interested to hear either way.\nStart now. \u0026amp;ldquo;The best time to plant a tree is twenty years ago. The second best time is now.\u0026amp;rdquo; Transitioning to programming was a leap for me. I wasn\u0026amp;rsquo;t sure it would work. Now most people assume I\u0026amp;rsquo;ve been doing it forever. It\u0026amp;rsquo;s …","permalink":"/posts/reflections-on-ten-years-professsionally-programming/","summary":"I recently hit a decade of professional programming. I\u0026rsquo;d like to take a moment here and reflect on what I\u0026rsquo;ve learned.\n","title":"Reflections on Ten Years Professionally Programming"},{"content":"Here\u0026amp;rsquo;s my annual professional review covering 2023.\nAccomplishments Work was fun this year! I spent it building software for veterinary clinics. Specifically: leading the redesign of our onboarding, dashboard, and product offerings on the web, and then building major portions of an internal information management tool.\nUnder \u0026amp;lsquo;boring and essential\u0026amp;rsquo;, I designed and implemented a comprehensive test suite that includes linting, formatting, and type checks, as well as unit and integration tests, and established continuous integration for an in-development application.\nIn soft …","permalink":"/posts/my-annual-review-2023/","summary":"Here\u0026rsquo;s my annual professional review covering 2023.\n","title":"My Annual Review 2023"},{"content":"React Router\u0026amp;rsquo;s Link component isn\u0026amp;rsquo;t just for internal links; it can work like an anchor tag, too, sending users outside the application. Use reloadDocument prop to get this behavior.\n\u0026amp;lt;Link reloadDocument to={\u0026amp;#39;https://reactrouter.com/en/main/components/link\u0026amp;#39;}\u0026amp;gt;Link docs\u0026amp;lt;/Link\u0026amp;gt; docs\n","permalink":"/tils/link-externally-with-react-routers-link/","summary":"React Router\u0026rsquo;s Link component isn\u0026rsquo;t just for internal links; it can work like an anchor tag, too, sending users outside the application. Use reloadDocument prop to get this behavior. …","title":"Link Externally With React Router's Link"},{"content":"When you combine cat (concatenate) with \u0026amp;gt; (output redirection), you can create and write to a file from the command line.\n$ cat \u0026amp;gt; test.txt Hit enter after the file name, and your terminal will wait for input. End the file with CTRL + D.\n$ cat \u0026amp;gt; test.txt one two three $ cat test.txt one two three \u0026amp;gt;\u0026amp;gt; works, too; appending to the file as expected.\n","permalink":"/tils/create-a-file-with-cat/","summary":"When you combine cat (concatenate) with \u0026gt; (output redirection), you can create and write to a file from the command line.\n$ cat \u0026gt; test.txt Hit enter after the file name, and your terminal will …","title":"Create a File With `cat`"},{"content":"With the new as const construct, we can derive a TypeScript union type from a JavaScript array!\nconst builderSteps = [ \u0026amp;#39;communications\u0026amp;#39;, \u0026amp;#39;estimates\u0026amp;#39;, \u0026amp;#39;health status\u0026amp;#39;, \u0026amp;#39;procedures\u0026amp;#39; ] as const; type BuilderStep = (typeof builderSteps)[number]; This lets us both type a slice of state, type its updater, and build JSX elements all from the same array, a virtuous cycle.\nconst [step, setStep] = useState\u0026amp;lt;BuilderStep\u0026amp;gt;(\u0026amp;#39;communications\u0026amp;#39;) const handleStepClick = (step: BuilderStep) =\u0026amp;gt; setStep(step) {builderSteps.map(step =\u0026amp;gt; \u0026amp;lt;button key={step} …","permalink":"/tils/typescript-union-type-from-array/","summary":"With the new as const construct, we can derive a TypeScript union type from a JavaScript array!\nconst builderSteps = [ \u0026#39;communications\u0026#39;, \u0026#39;estimates\u0026#39;, \u0026#39;health status\u0026#39;, …","title":"TypeScript Union Type From Array"},{"content":"When I was learning to program, I was fortunate to pair with very experienced engineers. One day while coding, I said: \u0026amp;ldquo;I think we need to use Ruby\u0026amp;rsquo;s map method, but I\u0026amp;rsquo;m not sure how that works. Let me look it up.\u0026amp;rdquo;\nLater, my pair offered some feedback: \u0026amp;ldquo;You can\u0026amp;rsquo;t be looking up map. You need to know how all of Ruby\u0026amp;rsquo;s Enumerable methods work.\u0026amp;rdquo;\nThis bit of radical candor was part of a series of challenges I faced early as a junior engineer. They shook me out of impending Expert Beginnerism. They expanded my concept of engineering excellence.\nHe was …","permalink":"/posts/you-cant-be-looking-up-map/","summary":"When I was learning to program, I was fortunate to pair with very experienced engineers. One day while coding, I said: \u0026ldquo;I think we need to use Ruby\u0026rsquo;s map method, but I\u0026rsquo;m not sure how …","title":"Master the Fundamentals First"},{"content":"Open Chrome DevTools, and click the three dots in the top right corner. Select \u0026amp;lsquo;More Tools \u0026amp;gt; Sensors\u0026amp;rsquo;. Under \u0026amp;lsquo;Location\u0026amp;rsquo;, set your timezone override.\n","permalink":"/tils/change-timezone-in-chrome-devtools/","summary":"Open Chrome DevTools, and click the three dots in the top right corner. Select \u0026lsquo;More Tools \u0026gt; Sensors\u0026rsquo;. Under \u0026lsquo;Location\u0026rsquo;, set your timezone override.\n","title":"Change Timezone in Chrome DevTools"},{"content":"When I don\u0026amp;rsquo;t feel like I\u0026amp;rsquo;m making sufficient progress at work, I have a favorite technique: asking \u0026amp;ldquo;What would finishing this today look like?\u0026amp;rdquo;\nIt\u0026amp;rsquo;s a version of one of Tim Ferriss\u0026amp;rsquo; favorite questions:\n\u0026amp;ldquo;\u0026amp;lsquo;What would this look like if it were easy?\u0026amp;rsquo; If I feel stressed, stretched thin, or overwhelmed, it\u0026amp;rsquo;s usually because I\u0026amp;rsquo;m overcomplicating something or failing to take the simple/easy path because I feel I should be trying \u0026amp;lsquo;harder\u0026amp;rsquo;\u0026amp;hellip;\u0026amp;rdquo;\nRecently, I was building a complex web form, with brand-new APIs, in a …","permalink":"/posts/what-would-finishing-this-today-look-like/","summary":"When I don\u0026rsquo;t feel like I\u0026rsquo;m making sufficient progress at work, I have a favorite technique: asking \u0026ldquo;What would finishing this today look like?\u0026rdquo;\n","title":"\"What Would Finishing This Today Look Like?\""},{"content":"A design principle that\u0026amp;rsquo;s crept into my programming could be summarized as:\n\u0026amp;ldquo;Absence of color is better than the wrong color.\u0026amp;rdquo;\nColor in user interfaces is hard. It\u0026amp;rsquo;s a powerful overlay to your design that can carry positive, negative, or conflicting messages. It should be carefully applied in isolation from other design choices.\nAn esteemed web designer I worked with once told me: \u0026amp;ldquo;Color is the last step of my designs.\u0026amp;rdquo; They\u0026amp;rsquo;d make grayscale mockups, then pixel-perfect renderings, choosing fonts, icons, buttons, layouts, etc. At the very end, …","permalink":"/posts/no-color-is-better-than-the-wrong-color/","summary":"A design principle that\u0026rsquo;s crept into my programming could be summarized as:\n\u0026ldquo;Absence of color is better than the wrong color.\u0026rdquo;\n","title":"Absence Of Color Is Better Than the Wrong Color"},{"content":"One of my favorite product hacks is asking: \u0026amp;ldquo;What\u0026amp;rsquo;s the why?\u0026amp;rdquo;\nAsking \u0026amp;ldquo;Why\u0026amp;rdquo; Before delivering a feature, stop and ask: why is this feature worth adding?\nWhy is it worth the time for your teammates to review, for your stakeholders to approve, and for your users to adjust to? What are we getting from adding it, that\u0026amp;rsquo;s better than anything else we could be building?\nIt\u0026amp;rsquo;s a shift to ownership. Here\u0026amp;rsquo;s non-ownership: \u0026amp;ldquo;Every app has a logout feature.\u0026amp;rdquo; And ownership: \u0026amp;ldquo;Our customers must be able to log out because we display sensitive …","permalink":"/posts/whats-the-why/","summary":"One of my favorite product hacks is asking: \u0026ldquo;What\u0026rsquo;s the why?\u0026rdquo;\n","title":"Product Technique: Asking 'What's the Why?'"},{"content":"Write a little bit of code, and you may come to an unsettling realization: there are multiple ways to do almost any programming task. How do we choose between several that work? I manage this uncertainty with a guideline: writing boring code. In this post, I\u0026amp;rsquo;ll try to explain what boring means to me.\nNot-Boring Example: JavaScript Function Sometimes programmers will write traditional JavaScript functions and arrow functions in the same codebase:\nfunction trim(input) { return input.trim(); }; const handleChange = (e) =\u0026amp;gt; setTitle(trim(e.target.value)); If this is your style, I …","permalink":"/posts/write-boring-code/","summary":"Write a little bit of code, and you may come to an unsettling realization: there are multiple ways to do almost any programming task. How do we choose between several that work? I manage this …","title":"Write Boring Code"},{"content":"Something I\u0026amp;rsquo;ve learned as an engineer: when presented with the option of working on a big project, or doing anything else, take the big project.\nThe Big Project™ is a new dashboard, an AR/VR/AI integration, an experimental mobile or hardware app, etc. They take multiple people days to plan, and at least a month to complete, possibly with multiple engineers. People on the team are excited (and possibly a bit intimidated) by the idea of being the lead on the Big Project.\nCounterarguments Let\u0026amp;rsquo;s address the counterarguments right away. Yes, Big Projects are harder to pull off. Yes, …","permalink":"/posts/take-the-big-project/","summary":"Something I\u0026rsquo;ve learned as an engineer: when presented with the option of working on a big project, or doing anything else, take the big project.\n","title":"Take the Big Project"},{"content":"We spent the time writing tests, and yet, a bug survived. Should we just stop writing tests? No, but we should maybe write better tests, and think about them differently.\nIn this post, I\u0026amp;rsquo;d like to explain a mental model I have that helps me write and maintain tests: thinking of bugs, and the tests that cover them, in classes.\nBugs and Tests in Classes Every test has limitations. Unit tests miss the big picture, end-to-end (E2E) tests miss the details. Rather than chasing the impossible, that the test suite catches all bugs, I\u0026amp;rsquo;ve found it\u0026amp;rsquo;s preferable to think about bugs and …","permalink":"/posts/thinking-of-bugs-in-classes/","summary":"We spent the time writing tests, and yet, a bug survived. Should we just stop writing tests? No, but we should maybe write better tests, and think about them differently.\n","title":"Thinking of Bugs in Classes"},{"content":"Have you ever seen a pull request that seems to completely explain itself? It\u0026amp;rsquo;s a real artifact. I don\u0026amp;rsquo;t know the project, yet I understand it. How can we get results like this on every pull request, from every developer on the team, every time?\nThe answer is a pull request template. Templates give your team a format that\u0026amp;rsquo;s detailed, discoverable, and predictable. In this post, I\u0026amp;rsquo;ll share this technique and why it matters.\nThe Pull Request Template Technique For Github, create a file in your repo like this:\n$ touch .github/pull_request_template.md The markdown contents …","permalink":"/posts/pull-request-templates/","summary":"Have you ever seen a pull request that seems to completely explain itself? It\u0026rsquo;s a real artifact. I don\u0026rsquo;t know the project, yet I understand it. How can we get results like this on every …","title":"Why Great PRs Are Great: Pull Requests Templates"},{"content":"Here\u0026amp;rsquo;s some advice about programming I\u0026amp;rsquo;ve found useful: \u0026amp;ldquo;Don\u0026amp;rsquo;t ask for advice; ask for a code review.\u0026amp;rdquo; In this post, I\u0026amp;rsquo;d like to explore what I think this adage means.\nThe Trick to Getting Better at Programming Isn\u0026amp;rsquo;t Advice When learning programming, we think there\u0026amp;rsquo;s a trick to it, that can be solved with advice. If I\u0026amp;hellip;\n\u0026amp;hellip;learn the right language, framework, or text editor \u0026amp;hellip;attend the best CS program, bootcamp, or workshop \u0026amp;hellip;master the ideal learning strategy or morning routine \u0026amp;hellip;I\u0026amp;rsquo;ll have an advantage. In my …","permalink":"/posts/dont-ask-for-advice-ask-for-a-code-review/","summary":"Here\u0026rsquo;s some advice about programming I\u0026rsquo;ve found useful: \u0026ldquo;Don\u0026rsquo;t ask for advice; ask for a code review.\u0026rdquo; In this post, I\u0026rsquo;d like to explore what I think this adage …","title":"Don't Ask for Advice; Ask for a Code Review"},{"content":"When it comes to controlling frontend presentation, developers often rely on booleans. However, this approach can be limiting. In this post, I’ll explore the drawbacks of using booleans and introduce a more versatile alternative: plain old strings with type safety.\nWhile this example uses React, the principles can be applied to other frontend frameworks as well.\nThe Setup The React useState hook gives us local state, and it\u0026amp;rsquo;s often instantiated with a boolean. Open any complex React codebase, and you are sure to see something like this:\nimport { useState } from \u0026amp;#39;react\u0026amp;#39;; const …","permalink":"/posts/from-boolean-to-strings-in-frontend-state/","summary":"When it comes to controlling frontend presentation, developers often rely on booleans. However, this approach can be limiting. In this post, I’ll explore the drawbacks of using booleans and introduce …","title":"From Booleans to Strings in Frontend State"},{"content":"In many group debugging sessions I\u0026amp;rsquo;ve joined, a major technique being deployed is something I call \u0026amp;ldquo;Try This\u0026amp;rdquo; debugging. In this post, I\u0026amp;rsquo;d like to talk about this anti-pattern, and consider a better way.\n\u0026amp;ldquo;Try This\u0026amp;rdquo; Debugging Defined What is \u0026amp;ldquo;try this\u0026amp;rdquo; debugging?\nImagine we\u0026amp;rsquo;re debugging as a pair and we\u0026amp;rsquo;re stuck. The easy answers aren\u0026amp;rsquo;t working.\nSuddenly, I produce an idea. It might be a:\nStack Overflow answer GitHub Gist Popular comment on a GitHub issue Slack post from a colleague I share this with you and say the magic words: …","permalink":"/posts/the-case-against-try-this-debugging/","summary":"In many group debugging sessions I\u0026rsquo;ve joined, a major technique being deployed is something I call \u0026ldquo;Try This\u0026rdquo; debugging. In this post, I\u0026rsquo;d like to talk about this anti-pattern, …","title":"The Case Against \"Try This\" Debugging"},{"content":"Absolute imports are an essential developer experience feature for me in any JavaScript application. In this post, I\u0026amp;rsquo;ll explain what they are, how to use them, and why they matter.\nWhat Is An Absolute Import? It\u0026amp;rsquo;s easier to think of an absolute import in contrast to a relative import, which looks like this:\nimport { Page } from \u0026amp;#39;../../views/pages/Page\u0026amp;#39;; Translating this code into words:\n\u0026amp;ldquo;Bring in the named export Page, which can be found \u0026amp;lsquo;up\u0026amp;rsquo; two directories and then inside the views/pages directory.\u0026amp;rdquo;\nHere\u0026amp;rsquo;s that same import with an absolute …","permalink":"/posts/the-joy-of-javascript-absolute-imports/","summary":"Absolute imports are an essential developer experience feature for me in any JavaScript application. In this post, I\u0026rsquo;ll explain what they are, how to use them, and why they matter.\n","title":"Absolute Imports in TypeScript and React: A Practical Guide"},{"content":"Over the past decade, I\u0026amp;rsquo;ve settled into a daily programming routine that helps me stay productive. It centers on code review, moving tickets forward, finishing in-progress work, and documenting what I learn. Here\u0026amp;rsquo;s the exact workflow I use.\nWhy Have a Routine at All? Many programmers don\u0026amp;rsquo;t have a routine. So, first let me answer: why have one at all? A routine at work can be a double-edged sword; overemphasize it, and your day can feel too predictable.\nThe first reason I keep a routine is that there are some things I must do every single day, like team standups. My day goes …","permalink":"/posts/my-daily-programming-routine/","summary":"Over the past decade, I\u0026rsquo;ve settled into a daily programming routine that helps me stay productive. It centers on code review, moving tickets forward, finishing in-progress work, and documenting …","title":"My Daily Programming Routine as a Senior Software Engineer"},{"content":"We\u0026amp;rsquo;re back with another SVGO configuration you\u0026amp;rsquo;ll probably want: disabling ID minification.\nSVGO has a default plugin called cleanupIds that takes every ID in the SVG (e.g. clip0_506_1332) and minifies it to a. This works okay until there are two different SVG\u0026amp;rsquo;s on the page. When that happens, the ID\u0026amp;rsquo;s become invalid and conflicting HTML and your images don\u0026amp;rsquo;t compose correctly.\nDisable this in the SVGO config file:\n// svgo.config.js module.exports = { plugins: [ { name: \u0026amp;#39;preset-default\u0026amp;#39;, params: { overrides: { cleanupIds: false, }, }, }, ], }; ","permalink":"/tils/install-svgo-with-reasonable-defaults-part-two/","summary":"We\u0026rsquo;re back with another SVGO configuration you\u0026rsquo;ll probably want: disabling ID minification.\nSVGO has a default plugin called cleanupIds that takes every ID in the SVG (e.g. clip0_506_1332) …","title":"Install SVGO With Reasonable Defaults, Part Two"},{"content":" There are only two hard things in Computer Science: cache invalidation and naming things. \u0026amp;ndash; Phil Karlton\nLet\u0026amp;rsquo;s talk about variable name antipatterns!\nI use the term \u0026amp;lsquo;antipattern\u0026amp;rsquo; a lot on this blog. To make sure I\u0026amp;rsquo;ve defined it at least once, an antipattern is a technique that seems good but has more bad consequences than good ones.\nThese are antipatterns because they often make sense in isolation and can propagate through a codebase, to the detriment of developer experience and maintainability of the codebase. If you\u0026amp;rsquo;re interested in a similar take on this …","permalink":"/posts/variable-name-antipatterns/","summary":" There are only two hard things in Computer Science: cache invalidation and naming things. \u0026ndash; Phil Karlton\nLet\u0026rsquo;s talk about variable name antipatterns!\n","title":"Variable Name Antipatterns Named and Explained"},{"content":"Code coverage, the percentage of your code covered by automated tests, is a metric associated with quality. In this post, I\u0026amp;rsquo;d like to investigate this association. And pour some cold water on it.\nThe higher-is-better metric I\u0026amp;rsquo;m discussing could be summarized as:\nCoverage percentage up: good job! Coverage percentage down: let\u0026amp;rsquo;s fix write more tests. Function covered by a test? Good. Function not covered by a test? Bad. This perspective is not a Straw Man argument; it\u0026amp;rsquo;s real! And here are the issues I see with it:\nTests are tradeoffs Coverage can measure the wrong thing …","permalink":"/posts/cold-water-for-your-code-coverage/","summary":"Code coverage, the percentage of your code covered by automated tests, is a metric associated with quality. In this post, I\u0026rsquo;d like to investigate this association. And pour some cold water on …","title":"Cold Water for Your Code Coverage"},{"content":"Here\u0026amp;rsquo;s an argument that\u0026amp;rsquo;s gaining momentum in my field: AI will eliminate or greatly reduce the programming profession.\nTo elaborate:\nAI like ChatGPT and GitHub Copilot is very good at writing describable code (\u0026amp;ldquo;translate feet to meters\u0026amp;rdquo;) This is the kind of code entry-level programmers are often asked to write AI could eliminate programmer jobs, particularly entry-level Is this correct? Great question! We\u0026amp;rsquo;re all watching this unprecedented phenomenon unfold. Predictions are plentiful.\nAs a mentor to new programmers, this argument matters to me, and I\u0026amp;rsquo;m …","permalink":"/posts/does-chat-gpt-mean-software-engineering-is-over/","summary":"Here\u0026rsquo;s an argument that\u0026rsquo;s gaining momentum in my field: AI will eliminate or greatly reduce the programming profession.\n","title":"Does ChatGPT Mean Software Engineering Is Over?"},{"content":"When you allow a user\u0026amp;rsquo;s behavior to add or remove a border from an element, that can cause the page to \u0026amp;lsquo;jump around\u0026amp;rsquo; as the border is added or removed. A common example is a border around an element that is \u0026amp;lsquo;selected\u0026amp;rsquo;.\nFix this issue by adding a transparent border around the element and giving it color when it\u0026amp;rsquo;s selected. No more jumps.\n.element { border: 1px solid transparent; } .selectedElement { border-color: $selected-color; } ","permalink":"/tils/apply-a-conditional-border-without-moving-element/","summary":"When you allow a user\u0026rsquo;s behavior to add or remove a border from an element, that can cause the page to \u0026lsquo;jump around\u0026rsquo; as the border is added or removed. A common example is a border …","title":"Apply a Conditional Border Without Moving Element"},{"content":"Need to undo a code change? You could revert. Or, you could apply the patch without creating a commit. That\u0026amp;rsquo;s what I want to do much of the time.\nUse git apply with the -R (reverse) flag:\ngit show 8cc13b1 | git apply -R All the changes from 8cc13b1 get applied to your working directory. In a large commit, do this tactically by limiting to just one file or directory.\ngit show 8cc13b1 src/fileToRevert.ts | git apply -R ","permalink":"/tils/apply-a-patch-in-reverse/","summary":"Need to undo a code change? You could revert. Or, you could apply the patch without creating a commit. That\u0026rsquo;s what I want to do much of the time.\nUse git apply with the -R (reverse) flag:\ngit …","title":"Apply a Patch in Reverse"},{"content":"A project I\u0026amp;rsquo;m working on uses SVGO to clean and minify SVG files. There\u0026amp;rsquo;s one problem: SVGO by default removes the viewBox attribute, which comes embedded in the SVG\u0026amp;rsquo;s I receive and have stylistic meaning in my codebase.\nYou can override this default by using an SVGO config file:\n// svgo.config.js module.exports = { plugins: [ { name: \u0026amp;#39;preset-default\u0026amp;#39;, params: { overrides: { removeViewBox: false, }, }, }, ], }; Applied like so:\nsvgo --config svgo.config.js input.svg --output output.svg This reduces my SVG\u0026amp;rsquo;s file size by about ~25%, without changing its …","permalink":"/tils/install-svgo-with-reasonable-defaults/","summary":"A project I\u0026rsquo;m working on uses SVGO to clean and minify SVG files. There\u0026rsquo;s one problem: SVGO by default removes the viewBox attribute, which comes embedded in the SVG\u0026rsquo;s I receive and …","title":"Install SVGO With Reasonable Defaults"},{"content":"Here are some one-sentence summaries of books that have helped me understand programming and technology.\nSome are technical and some are general. I\u0026amp;rsquo;m only including books here that I\u0026amp;rsquo;d recommend reading. This format is reductive by design, so please explore these books to consider the nuances I\u0026amp;rsquo;m omitting.\nOne-Sentence Summaries Beck, Kent and Andres, Cynthia. Extreme Programming Explained: Embrace Change. If an engineering practice is good (code review) we should do it all the time (pair programming). Carr, Nicholas. The Shallows: What the Internet Is Doing to Our Brains. …","permalink":"/posts/one-sentence-summaries-of-popular-technology-books/","summary":"Here are some one-sentence summaries of books that have helped me understand programming and technology.\n","title":"One-Sentence Summaries of Popular Technology Books"},{"content":"Retrospectives are one of my favorite engineering team practices. In this post, I\u0026amp;rsquo;ll explain why and how I run retros.\nI\u0026amp;rsquo;ll be covering:\nWhat is a retro? Why should we retro? Retro setup Running the retro Let\u0026amp;rsquo;s get started!\nWhat is a Retro? What is a retro? From a dictionary:\nRetro, short for retrospective: \u0026amp;lsquo;a surveying of the past.\u0026amp;rsquo;\nRetros are a Scrum/Agile \u0026amp;lsquo;ceremony\u0026amp;rsquo;, a collection of best practices for software engineering teams.\nI see a retro as a safe space for a team to pause work, discuss what\u0026amp;rsquo;s going well and what could be going better, …","permalink":"/posts/how-to-run-an-agile-retrospective-for-leaders/","summary":"Retrospectives are one of my favorite engineering team practices. In this post, I\u0026rsquo;ll explain why and how I run retros.\n","title":"How to Run an Agile Retrospective for Leaders"},{"content":"Autojump is my tool of choice for navigating directories. If I enter the following into the terminal:\n$ j til Autojump will try to guess where I want to go among directories I\u0026amp;rsquo;ve previously visited. It uses a database of directories and their relative rankings to do so.\nBut there\u0026amp;rsquo;s a problem! These day I only want to visit ~/oss/til. In the past I\u0026amp;rsquo;ve visited ~/oss/hr-til and ~/oss/tilex hundreds of times. Autojump always sends me there first. I don\u0026amp;rsquo;t want that!\n$ j til ~/oss/hr-til $ j til ~/oss/tilex $ j til ~/oss/hr-til After some source-diving I learned that Autojump …","permalink":"/tils/override-autojumps-directory-rankings/","summary":"Autojump is my tool of choice for navigating directories. If I enter the following into the terminal:\n$ j til Autojump will try to guess where I want to go among directories I\u0026rsquo;ve previously …","title":"Override Autojump's Directory Rankings"},{"content":"There\u0026amp;rsquo;s been a lot of recent discussion in the TypeScript community about typing functions. These arguments tend to take binary positions: always type your functions, or never type them unless the compiler demands it. Which is the best option?\nToday I\u0026amp;rsquo;d like to present one you might not have considered, that I think is just right:\nUse implicit types most of the time Type library functions Use Implicit Types Most of the Time Most of the time, we should use implicit types. Here\u0026amp;rsquo;s a React click handler with implicit typing:\nconst handleClick = () =\u0026amp;gt; { …","permalink":"/posts/type-your-typescript-library-functions/","summary":"There\u0026rsquo;s been a lot of recent discussion in the TypeScript community about typing functions. These arguments tend to take binary positions: always type your functions, or never type them unless …","title":"Type Your TypeScript Library Functions"},{"content":"Today in \u0026amp;ldquo;Let JavaScript Surprise You\u0026amp;rdquo;:\nlet array = [1, 20, 11, 10, 7, 17, 2]; console.log(array.sort()); \u0026amp;gt; [1, 10, 11, 17, 2, 20, 7] 😳 The numbers are not sorted as we might expect. What\u0026amp;rsquo;s going on here? From Mozilla\u0026amp;rsquo;s docs:\nThe default sort order is ascending, built upon converting the elements into strings, then comparing their sequences of UTF-16 code units values.\nNow this sorting makes sense! Luckily, sort accepts a function, so here\u0026amp;rsquo;s the sorting we intended:\nconst sortNumbers = (first, second) =\u0026amp;gt; first - second console.log(array.sort(sortNumbers)) …","permalink":"/tils/sorting-numbers-with-javascripts-sort-function/","summary":"Today in \u0026ldquo;Let JavaScript Surprise You\u0026rdquo;:\nlet array = [1, 20, 11, 10, 7, 17, 2]; console.log(array.sort()); \u0026gt; [1, 10, 11, 17, 2, 20, 7] 😳 The numbers are not sorted as we might expect. …","title":"Sorting Numbers with JavaScript's sort Function"},{"content":"CRUD is an acronym that\u0026amp;rsquo;s used a lot in web and mobile development. Frontend engineers in particular might wonder what it means and why we use it. In this post, I\u0026amp;rsquo;ll explain CRUD using PostgreSQL, and talk about why it\u0026amp;rsquo;s important.\nUnfamiliar with SQL or Postgres? No problem! These examples are pretty simple.\nWhat is CRUD? CRUD is an acronym that stands for \u0026amp;lsquo;create, read, update, and delete.\u0026amp;rsquo; In persistence, these are four operations we perform on a record. According to Wikipedia: \u0026amp;ldquo;[t]he term was likely first popularized by James Martin in his 1983 book …","permalink":"/posts/the-power-of-crud-operations-explained-with-sql/","summary":"CRUD is an acronym that\u0026rsquo;s used a lot in web and mobile development. Frontend engineers in particular might wonder what it means and why we use it. In this post, I\u0026rsquo;ll explain CRUD using …","title":"The Power of CRUD Operations Explained with SQL"},{"content":"Have you ever wanted to create a timer in a React app? This could be in support of a UI timer or polling. In this post, I\u0026amp;rsquo;ll explain how to create a timer effect in a React application using hooks and setInterval.\nLet\u0026amp;rsquo;s get right to the code. Below, I\u0026amp;rsquo;ll explain what every line is doing.\nimport * as React from \u0026amp;#34;react\u0026amp;#34;; import { useState } from \u0026amp;#34;react\u0026amp;#34;; const App = () =\u0026amp;gt; { const [timer, setTimer] = useState(); const toggleTimer = () =\u0026amp;gt; { if (timer) { clearInterval(timer); setTimer(undefined); } else { const timerId = setInterval(() =\u0026amp;gt; …","permalink":"/posts/how-to-create-a-simple-timer-in-react-with-setinterval/","summary":"Have you ever wanted to create a timer in a React app? This could be in support of a UI timer or polling. In this post, I\u0026rsquo;ll explain how to create a timer effect in a React application using …","title":"How to Create a Timer or Polling in React with setInterval"},{"content":"Have you ever wanted to take a JavaScript array of strings and turn them into a sentence? Here\u0026amp;rsquo;s how you do that.\nconst toLearn = [ \u0026amp;#39;object-oriented programming\u0026amp;#39;, \u0026amp;#39;frontend engineering\u0026amp;#39;, \u0026amp;#39;testing\u0026amp;#39;, ] const sentence = new Intl.ListFormat().format(toLearn) \u0026amp;gt; \u0026amp;#34;object-oriented programming, frontend engineering, and testing\u0026amp;#34; ListFormat takes options.\nThis has cross-browser support! The days of building a \u0026amp;rsquo;to sentence\u0026amp;rsquo; function in JavaScript are over.\n","permalink":"/tils/build-a-sentence-from-a-javascript-array/","summary":"Have you ever wanted to take a JavaScript array of strings and turn them into a sentence? Here\u0026rsquo;s how you do that.\nconst toLearn = [ \u0026#39;object-oriented programming\u0026#39;, \u0026#39;frontend …","title":"Build a Sentence from a JavaScript Array"},{"content":"When viewing a Figma design file, you can zoom in/out with CMD and up/down on a trackpad or magic mouse.\nScroll left or right with one-finger swipe left or right.\nZoom and scroll; along with adding comments and copying CSS, that\u0026amp;rsquo;s most of what I do in Figma.\nI\u0026amp;rsquo;m sure that these commands are useful in many other settings, but I find them especially valuable in a Figma file, which can feel otherwise challenging for me to navigate.\n","permalink":"/tils/zoom-and-scroll-in-figma/","summary":"When viewing a Figma design file, you can zoom in/out with CMD and up/down on a trackpad or magic mouse.\nScroll left or right with one-finger swipe left or right.\nZoom and scroll; along with adding …","title":"Zoom and Scroll in Figma"},{"content":"When preparing feature development work, I write Gherkin-style tickets, which follow the \u0026amp;ldquo;Given/When/Then\u0026amp;rdquo; format. Many people call these stories, and I do, too. This technique is incredibly effective! But why? In this post, I\u0026amp;rsquo;ll try to answer that question.\nI learned this technique watching the greats at Hashrocket. They\u0026amp;rsquo;ve been doing it this way since 2008 because it works.\nI have two complementary arguments for this technique: images and raw text hide complexity, while stories reveal complexity.\nImages and Raw Text Hide Complexity When I\u0026amp;rsquo;ve worked on teams that …","permalink":"/posts/given-when-then/","summary":"When preparing feature development work, I write Gherkin-style tickets, which follow the \u0026ldquo;Given/When/Then\u0026rdquo; format. Many people call these stories, and I do, too. This technique is …","title":"My Feature Writing Template: Given/When/Then"},{"content":"Which HTML elements should we be giving a user to click? In this post, I\u0026amp;rsquo;ll share some best practices for clickable elements.\nTo summarize:\nMost clickable elements should be links or buttons. Use buttons and links for their respective jobs. Clickable elements need signifiers. Let\u0026amp;rsquo;s get into it!\nMost Clickable Elements Should Be Links or Buttons Peruse a website\u0026amp;rsquo;s markup;, and you\u0026amp;rsquo;ll find many clickable elements that look like an HTML button, but aren\u0026amp;rsquo;t: div, span, tr, etc. Sometimes there are no actual buttons in an application at all!\nIf this post convinces you …","permalink":"/posts/button-link-or-neither/","summary":"Which HTML elements should we be giving a user to click? In this post, I\u0026rsquo;ll share some best practices for clickable elements.\n","title":"Button, Link, or Neither? Three Principles for Clarity and Accessibility"},{"content":"Great developers review their own code often and effectively. In this post, I\u0026amp;rsquo;ll share a checklist of all my best tips for maximizing this important practice.\n1. Wait As Long As You Can Wait as long as you can before reviewing your code.\nIn On Writing, Stephen King recounts that when he finishes a first draft, he puts it away and doesn\u0026amp;rsquo;t look at it for a long time. The closer we are to our work, the harder it is to see its flaws.\nSo, get some distance. Even ten minutes helps. I like to open PRs at the end of the day and self-review them first thing in the morning.\n2. Change the …","permalink":"/posts/six-tips-for-reviewing-your-own-code/","summary":"Great developers review their own code often and effectively. In this post, I\u0026rsquo;ll share a checklist of all my best tips for maximizing this important practice.\n","title":"All My Best Tips For Expertly Reviewing Your Own Code"},{"content":"I\u0026amp;rsquo;ve been lucky to have worked with some great engineers, and one thing that they tend to do exceptionally well is reporting about their work at meetings. Today I\u0026amp;rsquo;d like to summarize what I think makes a great standup report.\nMy goal is a report that\u0026amp;rsquo;s informative, impromptu, and actionable.\nInformative I try to consistently bring the expected information to my standup report. Time is limited and valuable.\nOn some teams, this is \u0026amp;ldquo;yesterday, today, blockers.\u0026amp;rdquo; I often omit yesterday\u0026amp;rsquo;s work; it\u0026amp;rsquo;s the past and likely less relevant. Same with blockers, or …","permalink":"/posts/deliver-a-great-standup-report-as-an-engineer/","summary":"I\u0026rsquo;ve been lucky to have worked with some great engineers, and one thing that they tend to do exceptionally well is reporting about their work at meetings. Today I\u0026rsquo;d like to summarize what …","title":"Deliver a Great Standup Report as an Engineer"},{"content":"As a mentor at Code Platoon, I\u0026amp;rsquo;ve been able to help many people transition into programming. In this post, I\u0026amp;rsquo;d like to focus on a vital part of that process: crafting your transitioning-to-tech resume.\nObstacle: Focusing on the Past The most common issue I see with transitioning resumes: too much focus on the past.\nA headline that says \u0026amp;ldquo;Transitioning Chef and Aspiring Web Developer.\u0026amp;rdquo;\nA summary that claims you are dabbling in programming while still running a kitchen.\nSkills that list pastries, southern cuisine, and JavaScript.\nBullet points for your previous roles that …","permalink":"/posts/resume-for-transitioning-to-programming/","summary":"As a mentor at Code Platoon, I\u0026rsquo;ve been able to help many people transition into programming. In this post, I\u0026rsquo;d like to focus on a vital part of that process: crafting your …","title":"How to Write a Resume for Transitioning to Programming"},{"content":"Today I completed the \u0026amp;lsquo;Resistor Color Trio\u0026amp;rsquo; TypeScript exercise on Exercism. Here\u0026amp;rsquo;s my solution.\nThis exercise asks us to write a TypeScript function that takes two color names (e.g. \u0026amp;lsquo;brown\u0026amp;rsquo; and \u0026amp;lsquo;black\u0026amp;rsquo;) and a power-of-ten multiplier color (e.g. \u0026amp;lsquo;black\u0026amp;rsquo;), returning the electrical resistance they would produce in string format (\u0026amp;lsquo;10 ohms\u0026amp;rsquo; in this example).\nMy Solution Here\u0026amp;rsquo;s the provided unit test:\nimport { decodedResistorValue } from \u0026amp;#39;./resistor-color-trio\u0026amp;#39; describe(\u0026amp;#39;Resistor Colors\u0026amp;#39;, () =\u0026amp;gt; { …","permalink":"/posts/resistor-color-trio-in-typescript/","summary":"Today I completed the \u0026lsquo;Resistor Color Trio\u0026rsquo; TypeScript exercise on Exercism. Here\u0026rsquo;s my solution.\n","title":"Solving Exercism's Resistor Color Trio in TypeScript"},{"content":"Today, I\u0026amp;rsquo;d like to dive deeper into a part of my practice: using a persisted text file for jotting things down. In this post, I\u0026amp;rsquo;ll explain the \u0026amp;lsquo;why\u0026amp;rsquo; behind this technique, and provide an updated version of it.\nYou can read the post I\u0026amp;rsquo;m following up on here.\nWhy Use a Scratchpad? Why use this technique? Here are a few reasons.\nI\u0026amp;rsquo;m a note-taker. The military trains soldiers to always have \u0026amp;ldquo;something to write on, and something to write with.\u0026amp;rdquo; When I see somebody in a meeting not taking notes, I wonder if they\u0026amp;rsquo;re paying attention and how …","permalink":"/posts/vim-scratchpad-part-2/","summary":"Today, I\u0026rsquo;d like to dive deeper into a part of my practice: using a persisted text file for jotting things down. In this post, I\u0026rsquo;ll explain the \u0026lsquo;why\u0026rsquo; behind this technique, and …","title":"Vim Scratchpad Part 2"},{"content":"Some advice I offer people entering the programming profession is to buy the best hardware you can. You\u0026amp;rsquo;re going to need it.\nGet the most powerful computer, the biggest monitor, the best keyboard, mouse, headset, microphone, and webcam your budget allows. If you have to choose one, invest in the computer and collect the peripherals later.\nIn other desk jobs, there\u0026amp;rsquo;s a frugality about hardware. \u0026amp;ldquo;I\u0026amp;rsquo;m just editing spreadsheets– I don\u0026amp;rsquo;t need a powerful computer.\u0026amp;rdquo; Programming is different. It\u0026amp;rsquo;s resource-intensive work. In the web development, not only are …","permalink":"/posts/buy-the-best-hardware-you-can/","summary":"Some advice I offer people entering the programming profession is to buy the best hardware you can. You\u0026rsquo;re going to need it.\n","title":"Buy the Best Hardware You Can"},{"content":"Today as a code kata I completed the \u0026amp;lsquo;Resistor Color Duo\u0026amp;rsquo; TypeScript exercise on Exercism. Here\u0026amp;rsquo;s my work.\nThis exercise asks us to write a TypeScript function that takes two color names (e.g. \u0026amp;lsquo;brown\u0026amp;rsquo; and \u0026amp;lsquo;black\u0026amp;rsquo;) and returns the electrical resistance they would produce on a resistor as a number (10 in this example).\nMy Goals I approached this challenge with the following goals:\nSolve the problem Learn TypeScript better Leverage implicit typing Here\u0026amp;rsquo;s the spec and my solution.\nSolution Here\u0026amp;rsquo;s spec that describes the desired behavior.\nimport …","permalink":"/posts/resistor-color-duo-in-typescript/","summary":"Today as a code kata I completed the \u0026lsquo;Resistor Color Duo\u0026rsquo; TypeScript exercise on Exercism. Here\u0026rsquo;s my work.\n","title":"Solving Exercism's Resistor Color Duo in TypeScript"},{"content":"The exit code of a command in a Unix-based system is an important and easy-to-miss piece of data. It isn\u0026amp;rsquo;t printed to standard out; you have to go looking for it. I find it useful to inspect this information when debugging or considering chaining unfamiliar commands.\nTo see the exit code of your last command, echo its variable ($) value:\n$ echo \u0026amp;#34;something\u0026amp;#34; \u0026amp;#34;something\u0026amp;#34; $ echo $? 0 # Success! # \u0026amp;#39;1\u0026amp;#39; example $ rm nonexistent-file rm: nonexistent-file: No such file or directory $ echo $? 1 # Failure. The two exit codes you must remember are 0, command was successful, …","permalink":"/posts/print-last-exit-code/","summary":"The exit code of a command in a Unix-based system is an important and easy-to-miss piece of data. It isn\u0026rsquo;t printed to standard out; you have to go looking for it. I find it useful to inspect …","title":"Print Last Exit Code"},{"content":"I saw a meme this week: a person debugging code, \u0026amp;ldquo;My code doesn\u0026amp;rsquo;t work. Let\u0026amp;rsquo;s change nothing and run it again.\u0026amp;rdquo;\nThis is something that I\u0026amp;rsquo;ve done. It seems pointless. But that\u0026amp;rsquo;s not quite correct.\nRepeatedly \u0026amp;ldquo;running it again\u0026amp;rdquo;, by loading a page, printing something, or starting an automated test, without making a prediction– yes, this is a bad practice. But sometimes trying something twice is a solid technique. The reason is that even if you don\u0026amp;rsquo;t make a change, that doesn\u0026amp;rsquo;t mean that nothing has changed.\nWhat You Can Learn Let\u0026amp;rsquo;s …","permalink":"/posts/you-cant-change-nothing/","summary":"I saw a meme this week: a person debugging code, \u0026ldquo;My code doesn\u0026rsquo;t work. Let\u0026rsquo;s change nothing and run it again.\u0026rdquo;\nThis is something that I\u0026rsquo;ve done. It seems pointless. But …","title":"You Can't Change Nothing"},{"content":"When learning a new language or framework, the first thing I do is read the official documentation cover-to-cover.\nChoose the latest version, or if you\u0026amp;rsquo;re stuck with an older version, go with that.\nFor some tools like Go or React, this can be done in an hour or two. For others like Ruby, it\u0026amp;rsquo;s more of a challenge, so try to focus on the standard library or official guides. Read quickly, try stuff out, jot down a few notes.\nThe reason I find this method effective is that, although a lot of what you read won\u0026amp;rsquo;t totally click, you\u0026amp;rsquo;re laying a foundation. You\u0026amp;rsquo;re …","permalink":"/posts/read-the-docs/","summary":"When learning a new language or framework, the first thing I do is read the official documentation cover-to-cover.\n","title":"Read the Docs"},{"content":"In this post, I\u0026amp;rsquo;ll explain what factories are and why you want them, using Ruby.\nThe FactoryBot gem is ubiquitous in Ruby and Ruby on Rails testing. If you aren\u0026amp;rsquo;t familiar with it, you might be wondering, what\u0026amp;rsquo;s the point? Wouldn\u0026amp;rsquo;t it be simpler to build objects myself?\nAn Example Let\u0026amp;rsquo;s say we\u0026amp;rsquo;re unit testing a full_name method on a User class. full_name takes two pieces of data, first_name and last_name, and combines them with a space. Let\u0026amp;rsquo;s also assume that we\u0026amp;rsquo;ve tried to ensure data integrity by requiring the presence of these two fields.\n# …","permalink":"/posts/why-use-factorybot/","summary":"In this post, I\u0026rsquo;ll explain what factories are and why you want them, using Ruby.\n","title":"The Factory Pattern, Explained in Ruby"},{"content":"Trying to enforce unconventional coding preferences on a team is an uphill battle. When in doubt, default to convention.\nContrived Example Consider this contrived example. Imagine that you don\u0026amp;rsquo;t like Ruby on Rails delegators, which let you call a method on an instance through another instance. Here\u0026amp;rsquo;s an example:\nclass Greeter \u0026amp;lt; ActiveRecord::Base def hello \u0026amp;#39;hello\u0026amp;#39; end end class General \u0026amp;lt; ActiveRecord::Base belongs_to :greeter delegate :hello, to: :greeter end General.new.hello # =\u0026amp;gt; \u0026amp;#34;hello\u0026amp;#34; .hello is not defined on class General, yet we can call it because …","permalink":"/posts/default-to-convention/","summary":"Trying to enforce unconventional coding preferences on a team is an uphill battle. When in doubt, default to convention.\n","title":"Default to Programming Convention"},{"content":"Here are some books that have helped me understand programming. This list is a living document that I plan to update as I read.\nRecommended Reading Beck, Kent and Andres, Cynthia. Extreme Programming Explained: Embrace Change. XP won, and this book from Kent Beck is the primary source. I revisit it often to find or refresh my ideas about software delivery.\nHoover, Dave and Oshineye, Adewale. Apprenticeship Patterns: Guidance for the Aspiring Software Craftsman. Dave and Adewale provide a template for helping yourself or your teammates grow in their early careers. Some ideas that have greatly …","permalink":"/posts/recommended-reading/","summary":"Here are some books that have helped me understand programming. This list is a living document that I plan to update as I read.\n","title":"Recommended Reading"},{"content":"How do you introduce new ways of working? We want to bring a new idea to our engineering teams, such as a new command-line configuration, testing tool, or design pattern. But engineers can be discerning and stubborn customers. The idea needs to stand on its own. And, you have to sell it.\nHere are some approaches that I\u0026amp;rsquo;ve tried to introduce new ideas to a team. Combine them all, and you\u0026amp;rsquo;re on a good path.\n\u0026amp;ldquo;What\u0026amp;rsquo;s the Why?\u0026amp;rdquo; Before pitching an idea to your team, figure out why you want to do it.\nWhat problem is it solving? Who is it helping? What\u0026amp;rsquo;s lacking …","permalink":"/posts/how-to-introduce-new-ideas/","summary":"How do you introduce new ways of working? We want to bring a new idea to our engineering teams, such as a new command-line configuration, testing tool, or design pattern. But engineers can be …","title":"How to Introduce New Ideas"},{"content":"A big mental leap I made early in my programming was starting to view computers as something that could be understood.\nWhat is a computer? It\u0026amp;rsquo;s a piece of silicon. You can recreate a basic version of it with water or even Minecraft redstone. Each abstraction on top of that piece of silicon is a set of instructions that combine into something remarkable. Something that you, with enough time, can understand.\nTo put it another way: computers are not magical.\nI\u0026amp;rsquo;m not talking about that magical feeling you might get by solving a tough problem with code. That is magical, and may it stay …","permalink":"/posts/computers-can-be-understood/","summary":"A big mental leap I made early in my programming was starting to view computers as something that could be understood.\n","title":"Computers Can Be Understood"},{"content":"You\u0026amp;rsquo;ve been iterating on your Tmux configuration file, and now you want to your configs in action without closing the program. But how?\nHere\u0026amp;rsquo;s the command to source an updated tmux.conf:\n$ tmux source-file tmux.conf ","permalink":"/tils/source-tmux-config/","summary":"You\u0026rsquo;ve been iterating on your Tmux configuration file, and now you want to your configs in action without closing the program. But how?\nHere\u0026rsquo;s the command to source an updated tmux.conf:\n$ …","title":"Source Tmux Config"},{"content":"Last week we encountered a breaking change in our Tmux configuration, and briefly lost the ability to scroll with the mouse. It became an opportunity to re-learn Tmux\u0026amp;rsquo;s copy mode, and liberate ourselves from the mouse a little more.\nEnter copy mode with \u0026amp;lt;prefix\u0026amp;gt; [. From here, you can navigate with standard Vim commands.\nctrl-u moves you a half-page up, perfect for wading through test failure backtraces.\n","permalink":"/tils/tmux-copy-mode/","summary":"Last week we encountered a breaking change in our Tmux configuration, and briefly lost the ability to scroll with the mouse. It became an opportunity to re-learn Tmux\u0026rsquo;s copy mode, and liberate …","title":"Tmux Copy Mode"},{"content":"Today I\u0026amp;rsquo;d like to share a small part of my practice: using a persisted scratchpad text file for jotting things down.\nWhen coding, often I\u0026amp;rsquo;ll need to save a piece of data or type something out. This could be the identifier of a record, or a list of steps my pair and I think we need to take to fix a problem.\nInstead of creating a new file each time, I keep a scratchpad in my root directory, named ~/buffer. I\u0026amp;rsquo;ve mapped Vim to open it from normal mode with \u0026amp;lt;leader\u0026amp;gt; g:\n\u0026amp;#34; Open my scratchpad nnoremap \u0026amp;lt;leader\u0026amp;gt;q :e ~/buffer\u0026amp;lt;cr\u0026amp;gt; You can enter the file in insert …","permalink":"/posts/vim-scratchpad/","summary":"Today I\u0026rsquo;d like to share a small part of my practice: using a persisted scratchpad text file for jotting things down.\n","title":"Vim Scratchpad"},{"content":"One of my all-time favorite programming tools is Dash by Kapeli. In this post, I\u0026amp;rsquo;ll share some tips for leveraging this fantastic program.\nThe ideal audience for this post is someone who uses Dash and wants to use it better. But in case you haven\u0026amp;rsquo;t tried it yet, let me answer: what is Dash?\nWhat\u0026amp;rsquo;s Dash? Dash is a MacOS application that offers offline access to, and a user interface for searching, hundreds of API documentation sets.\nWhy is that useful? I\u0026amp;rsquo;ve learned that being able to find and share documentation effortlessly is a hallmark of a great engineer. Those who …","permalink":"/posts/level-up-your-dash/","summary":"One of my all-time favorite programming tools is Dash by Kapeli. In this post, I\u0026rsquo;ll share some tips for leveraging this fantastic program.\n","title":"Improve Your API Documentation Workflow with Dash"},{"content":"When testing with data, I prefer realistic data rather than random data.\nHere\u0026amp;rsquo;s the common pattern I\u0026amp;rsquo;m challenging: when testing forms, data presentation, or even seeding data, a programmer (often in a rush) will enter gibberish or jargon instead of something like a real user would enter.\nAn example might be an address form, where we mash some combination of the home row letters into each field. In this case, I prefer to enter \u0026amp;ldquo;123 Milwaukee Ave.\u0026amp;rdquo; or similar into the first address field, \u0026amp;ldquo;Chicago\u0026amp;rdquo; in the city field, and so on.\nOr in a CMS data field for a …","permalink":"/posts/prefer-real-data/","summary":"When testing with data, I prefer realistic data rather than random data.\n","title":"Prefer Real Data For Software Development"},{"content":"I believe that luck is part of every professional journey. Here\u0026amp;rsquo;s a sampling of the luck I\u0026amp;rsquo;ve had.\nA passage I recently read caught my attention:\n\u0026amp;ldquo;One of the first things [Stewart] Butterfield [(founder of Slack)] wants to know about when interviewing candidates for a position isn\u0026amp;rsquo;t which programming language they know or where their computer science degree is from. It\u0026amp;rsquo;s whether they believe luck played a role in getting them where they are\u0026amp;ndash; whether they think their success is a product not just of merit and talent, but also good circumstances. His goal is …","permalink":"/posts/luck-played-a-role/","summary":"I believe that luck is part of every professional journey. Here\u0026rsquo;s a sampling of the luck I\u0026rsquo;ve had.\n","title":"How Luck Played a Role in my Career"},{"content":"This is a list of all the best debugging tips I\u0026amp;rsquo;ve picked up over the years. Some of these might seem obvious, yet we forget them when it counts. Debugging is a skill. You have to bring every tool you have to the job.\nI\u0026amp;rsquo;ve divided this list into three sections: things to do before you start debugging, things to do during debugging, and things to do after the bug is fixed.\nA piece of advice that eclipses all others: be in the best mental condition possible before tackling a tricky bug. If you\u0026amp;rsquo;re tired, hungry, have a headache, or are otherwise distracted, it\u0026amp;rsquo;s going to …","permalink":"/posts/all-my-best-debugging-tips/","summary":"This is a list of all the best debugging tips I\u0026rsquo;ve picked up over the years. Some of these might seem obvious, yet we forget them when it counts. Debugging is a skill. You have to bring every …","title":"All My Best Debugging Tips"},{"content":"Let\u0026amp;rsquo;s say you have a string evaluated into a HTML template that contains newlines, like \u0026amp;quot;getting\\n\\things\\ndone\u0026amp;quot; and you want those newlines to be reflected in the presentation. Like this:\ngetting things done We can solve this with the following CSS on the string\u0026amp;rsquo;s containing element:\nwhite-space: pre-wrap; With pre-wrap, \u0026amp;ldquo;[s]equences of white space are preserved. Lines are broken at newline characters, at [breaks] and as necessary to fill line boxes.\u0026amp;rdquo;\nwhite-space docs\n","permalink":"/tils/preserve-whitespace-in-css/","summary":"Let\u0026rsquo;s say you have a string evaluated into a HTML template that contains newlines, like \u0026quot;getting\\n\\things\\ndone\u0026quot; and you want those newlines to be reflected in the presentation. Like …","title":"Preserve Whitespace in CSS"},{"content":"My development environment depends on several processes running. Here\u0026amp;rsquo;s how I\u0026amp;rsquo;ve scripted this setup.\nWhenever I boot up, I need at least these processes running:\nSidekiq (jobs) Rails (API) Webpack (frontend) Docker (containers) Here\u0026amp;rsquo;s how I\u0026amp;rsquo;ve scripted it:\n#!/bin/bash tmux new-window -n sidekiq tmux send-keys -t sidekiq \u0026amp;#39;sidekiq\u0026amp;#39; Enter tmux new-window -n rails tmux send-keys -t rails \u0026amp;#39;rails s\u0026amp;#39; Enter tmux new-window -n webpack tmux send-keys -t webpack \u0026amp;#39;bin/webpack\u0026amp;#39; Enter tmux new-window -n docker tmux send-keys -t docker \u0026amp;#39;bin/docker\u0026amp;#39; …","permalink":"/posts/tmux-application-startup-script/","summary":"My development environment depends on several processes running. Here\u0026rsquo;s how I\u0026rsquo;ve scripted this setup.\n","title":"Tmux Application Startup Script"},{"content":"Perhaps you\u0026amp;rsquo;re familiar with this scenario: you\u0026amp;rsquo;re debugging and stuck. You\u0026amp;rsquo;ve Googled, read some blog posts and docs. You return to your search engine, type some characters, and then something strange happens: the search engine autocompletes your question, and the results are all purple because they have been visited by you. I\u0026amp;rsquo;ve come to recognize this moment as always a symptom of a broken mental model.\nA broken mental model is a fundamentally flawed understanding of how something works. It derails problem-solving by leading us to ask bad questions. Bad questions …","permalink":"/posts/broken-mental-model/","summary":"Perhaps you\u0026rsquo;re familiar with this scenario: you\u0026rsquo;re debugging and stuck. You\u0026rsquo;ve Googled, read some blog posts and docs. You return to your search engine, type some characters, and …","title":"Recognizing and Repairing a Broken Mental Model"},{"content":"Action items are small, defined, actionable TODOs to follow up on after the meeting. An example: \u0026amp;ldquo;close all pull requests opened more than 90 days ago.\u0026amp;rdquo; Agile retrospectives should produce many of these.\nWhy Are They Important? Action items steer a retro away from conversation and toward action.\nI once worked with an engineer who ran retros among all the contractors on the project. He\u0026amp;rsquo;d push for action items after every discussion, saying: \u0026amp;ldquo;I want to respect everyone\u0026amp;rsquo;s time by making sure this leads to action.\u0026amp;rdquo;\nThat\u0026amp;rsquo;s my argument. When people show up …","permalink":"/posts/retros-need-action-items/","summary":"Action items are small, defined, actionable TODOs to follow up on after the meeting. An example: \u0026ldquo;close all pull requests opened more than 90 days ago.\u0026rdquo; Agile retrospectives should produce …","title":"Retros Need Action Items"},{"content":"There\u0026amp;rsquo;s almost always a better variable name or value than \u0026amp;lsquo;foo\u0026amp;rsquo;. It\u0026amp;rsquo;s useful as a debugging placeholder, but it almost never belongs in production code, even and especially in automated tests.\nWhat is \u0026amp;lsquo;foo\u0026amp;rsquo;? The Jargon File calls \u0026amp;lsquo;foo\u0026amp;rsquo; a metasyntactic variable: \u0026amp;ldquo;[a] name used in examples and understood to stand for whatever thing is under discussion, or any random member of a class of things under discussion.\u0026amp;rdquo;\nTwo programmers stand at a whiteboard, working on an authentication feature. One says:\n\u0026amp;ldquo;Our customer logs into the site …","permalink":"/posts/better-than-foo/","summary":"There\u0026rsquo;s almost always a better variable name or value than \u0026lsquo;foo\u0026rsquo;. It\u0026rsquo;s useful as a debugging placeholder, but it almost never belongs in production code, even and especially in …","title":"Better Than 'foo'"},{"content":"I think a great database migration is: correct, atomic, reversible, consistent with the style of the application, and (mostly) uncoupled from your application and RDBMS.\nThese ideas are framework-agnostic, but as an example, we\u0026amp;rsquo;ll use this migration from the Ruby on Rails documentation:\nclass CreateProducts \u0026amp;lt; ActiveRecord::Migration def change create_table :products do |t| t.string :name t.text :description t.timestamps end end end Correct After running each migration, it\u0026amp;rsquo;s important to inspect the database schema to ensure that the correct changes are present. I\u0026amp;rsquo;ve seen …","permalink":"/posts/database-migration-checklist/","summary":"I think a great database migration is: correct, atomic, reversible, consistent with the style of the application, and (mostly) uncoupled from your application and RDBMS.\n","title":"Database Migration Checklist"},{"content":"When printing a JavaScript value to the console, I suggest using an object literal over the raw value.\n// Okay console.log(response) // Better console.log({ response }) It\u0026amp;rsquo;s a little thing that over time has a big benefit. In this post, I\u0026amp;rsquo;ll explain this technique and describe why I find it useful.\nFirst Iteration: The Raw Value Imagine you have a value assigned to the variable response, and you want to know its value. Here\u0026amp;rsquo;s how you might print it to the console, with console.log.\nconsole.log(response) This is what 99% of devs (including me, often) are going to do, and …","permalink":"/posts/logging-an-object/","summary":"When printing a JavaScript value to the console, I suggest using an object literal over the raw value.\n","title":"Logging an Object in JavaScript"},{"content":"Why should someone learn Ruby in 2022? Ruby was my first programming language, and although I\u0026amp;rsquo;ve drifted elsewhere, I write Ruby every day. Many people in my network are Ruby diehards. As a result, it\u0026amp;rsquo;s been a long time since I\u0026amp;rsquo;ve gotten the chance to sell someone on Ruby.\nI\u0026amp;rsquo;d recommend Ruby because it is optimized for developer happiness, it\u0026amp;rsquo;s an exceptional scripting language, it grants access to Ruby on Rails, it has a fantastic community, and it provides stable work.\nNo language is perfect. Ruby has been critiqued by many, and I won\u0026amp;rsquo;t rehash or challenge …","permalink":"/posts/why-ruby/","summary":"Why should someone learn Ruby in 2022? Ruby was my first programming language, and although I\u0026rsquo;ve drifted elsewhere, I write Ruby every day. Many people in my network are Ruby diehards. As a …","title":"Why Ruby?"},{"content":"A common React pattern is to pass an object as a prop. Let\u0026amp;rsquo;s consider the ways this can be used and tradeoffs of each.\nConsider a user avatar component that shows the user\u0026amp;rsquo;s initials. You might see one in the upper right of your screen right now.\nWhat pieces of a user model should we pass to an Avatar component to achieve this?\nThe initials? (\u0026amp;quot;JW\u0026amp;quot;) The first and last names of the user? (\u0026amp;quot;Jake Worth\u0026amp;quot;) Or should we pass the entire user object? (user) Read on to see each in detail.\nPassing Just the Initials We could pass just the initials.\n\u0026amp;lt;Avatar …","permalink":"/posts/objects-as-props/","summary":"A common React pattern is to pass an object as a prop. Let\u0026rsquo;s consider the ways this can be used and tradeoffs of each.\n","title":"What Are the Tradeoffs of Passing Objects as React Props?"},{"content":"A naive implementation is a programming technique that prioritizes imperfect shortcuts for the sake of speed, simplicity, or lack of knowledge.\nNaive Implementation Example What is \u0026amp;rsquo;naive\u0026amp;rsquo;? Here\u0026amp;rsquo;s the dictionary definition:\nnaive (adjective)\nmarked by unaffected simplicity : ARTLESS, INGENUOUS \u0026amp;ndash;Merriam-Webster Dictionary Let\u0026amp;rsquo;s see it in a programming context!\nA pangram is a sentence that contains all the letters of the alphabet, such as: \u0026amp;ldquo;The quick brown fox jumps over the lazy dog.\u0026amp;rdquo; Consider this problem I borrowed from Exercism, asking us to build a …","permalink":"/posts/naive-implementation/","summary":"A naive implementation is a programming technique that prioritizes imperfect shortcuts for the sake of speed, simplicity, or lack of knowledge.\n","title":"Naive Implementation: The Art of Artless Programming"},{"content":"The local environment is a great place to iterate on a CircleCI configuration. You\u0026amp;rsquo;ll catch errors faster than pushing to CI, and help drive a fast, iterative configuration process.\nInstall the CircleCI CLI via Homebrew (or any package manager of your choice):\n$ brew install circleci Then, in the same directory as your .circleci/config.yml, run:\n$ circleci config validate # Config file at .circleci/config.yml is valid ","permalink":"/tils/validate-circleci-configuration-locally/","summary":"The local environment is a great place to iterate on a CircleCI configuration. You\u0026rsquo;ll catch errors faster than pushing to CI, and help drive a fast, iterative configuration process.\nInstall the …","title":"Validate CircleCI Configuration Locally"},{"content":"When I upgrade ASDF, I sometimes get an error like this when I try to run a command:\n/Users/jakeworth/.asdf/shims/jq: line 3: /usr/local/Cellar/asdf/0.9.0/libexec/bin/asdf: No such file or directory The issue is that I\u0026amp;rsquo;ve upgraded ADSF to v0.10.2, but the jq shim doesn\u0026amp;rsquo;t know that and can\u0026amp;rsquo;t find it in a directory called \u0026amp;ldquo;0.9.0\u0026amp;rdquo;. This issue persists even after running asdf reshim.\nMy solution is to force ASDF to rebuild shims directory by renaming it. A rename lets me get back to where I am now, in case I need to.\n$ mv ~/.asdf/shims ~/.asdf/shims-bak $ asdf reshim …","permalink":"/tils/force-asdf-reshim/","summary":"When I upgrade ASDF, I sometimes get an error like this when I try to run a command:\n/Users/jakeworth/.asdf/shims/jq: line 3: /usr/local/Cellar/asdf/0.9.0/libexec/bin/asdf: No such file or directory …","title":"Force ASDF Reshim"},{"content":"Authentication and authorization are two distinct concepts. Yet, I\u0026amp;rsquo;ve found they\u0026amp;rsquo;re sometimes used interchangeably. In this post, I\u0026amp;rsquo;d like to define these terms.\nAuthentication is verifying who someone is. Authorization is verifying what someone may access.\nAuthentication Authentication is the process of verifying who someone is.\nConsider a person trying to enter an airport terminal. TSA says: \u0026amp;ldquo;Who are you?\u0026amp;rdquo; and the person presents an ID. TSA verifies it\u0026amp;rsquo;s a legitimate ID and that the person presenting it is the person shown on the ID. When both are true, we …","permalink":"/posts/authentication-vs-authorization/","summary":"Authentication and authorization are two distinct concepts. Yet, I\u0026rsquo;ve found they\u0026rsquo;re sometimes used interchangeably. In this post, I\u0026rsquo;d like to define these terms.\n","title":"Authentication vs. Authorization"},{"content":"I write each post for this blog in Vim. Writing in the terminal makes me feel like a programmer, even when I\u0026amp;rsquo;m not specifically programming. In this post, I\u0026amp;rsquo;ll share how I spellcheck in Vim.\nEnable Spelling Vim\u0026amp;rsquo;s spelling help page is extensive, and can be viewed at:\n:help spell It suggests enabling spelling like this:\n:setlocal spell spelllang=en_us When you to this, you\u0026amp;rsquo;ll see words that Vim considers misspelled in red.\nI enable spellchecking always in the markdown files I use to write this blog via a group in my .vimrc:\n\u0026amp;#34; ~/.vimrc augroup filetype_docs autocmd! …","permalink":"/posts/spell-check-with-vim/","summary":"I write each post for this blog in Vim. Writing in the terminal makes me feel like a programmer, even when I\u0026rsquo;m not specifically programming. In this post, I\u0026rsquo;ll share how I spellcheck in …","title":"Spell Checking with Vim: Tutorial and Best Practices"},{"content":"I use Vim\u0026amp;rsquo;s spell feature when writing blog posts. Very often, the first suggestion it makes is the one that I choose. Consider a word like \u0026amp;lsquo;Rubysit.\u0026amp;rsquo; My dictionary contains the word \u0026amp;lsquo;Rubyist\u0026amp;rsquo; (naturally) and that\u0026amp;rsquo;s what Vim spell is going to suggest to me first. For transposition errors like this I\u0026amp;rsquo;d like to skip the word picker when possible.\nTo automatically choose the first suggestion, in normal mode put your cursor over the word and type 1z=.\n","permalink":"/tils/select-first-spell-suggestion/","summary":"I use Vim\u0026rsquo;s spell feature when writing blog posts. Very often, the first suggestion it makes is the one that I choose. Consider a word like \u0026lsquo;Rubysit.\u0026rsquo; My dictionary contains the word …","title":"Select First Spell Suggestion"},{"content":"To hide the bookmarks bar in Google Chrome:\nPress CMD + SHIFT + B on Mac\nPress CTRL + SHIFT + B on Windows\nThis shortcut toggles the bookmarks bar on or off.\nI don\u0026amp;rsquo;t like including my Chrome bookmarks in browser screenshots; they are irrelevant to my work and sometimes personal. This hotkey toggles hiding or showing the Chrome browser bookmarks bar. My workflow is toggle off the bookmarks, take a clean browser screenshot, and then toggle them back on.\n","permalink":"/tils/hide-bookmarks-bar-in-chrome/","summary":"To hide the bookmarks bar in Google Chrome:\nPress CMD + SHIFT + B on Mac\nPress CTRL + SHIFT + B on Windows\nThis shortcut toggles the bookmarks bar on or off.\n","title":"Hide Bookmarks Bar in Chrome"},{"content":"Bugs are part of software. Reporting bugs well is a necessary skill in an ever-growing number of job titles. In this post, I\u0026amp;rsquo;d like to explain how to write a perfect bug report and offer some bug-reporting best practices.\nWhat do I mean by \u0026amp;lsquo;perfect\u0026amp;rsquo;? A perfect bug report allows the receiver to start effectively solving the problem without additional conversation. I\u0026amp;rsquo;ve found that it\u0026amp;rsquo;s the standard on great teams.\nMy Bug Report Template Teams that are great at this tend to have one of these:\nA bug report template that\u0026amp;rsquo;s pre-populated A form or wizard to …","permalink":"/posts/a-perfect-bug-report/","summary":"Bugs are part of software. Reporting bugs well is a necessary skill in an ever-growing number of job titles. In this post, I\u0026rsquo;d like to explain how to write a perfect bug report and offer some …","title":"How to Write a Perfect Bug Report (With Template)"},{"content":"Imagine you\u0026amp;rsquo;re debugging, and you\u0026amp;rsquo;re stuck. I have a technique that\u0026amp;rsquo;s going to help. Think of an action you might take. Predict what will happen when you take that action. Take the action. Check if you were right or wrong, consider that information, and repeat.\nThis may sound simple, but it\u0026amp;rsquo;s incredibly powerful. Here\u0026amp;rsquo;s how it works.\nMaking a Prediction Imagine you\u0026amp;rsquo;re debugging a web application, and you\u0026amp;rsquo;re trying to understand why some data on your page looks wrong.\nThink of an action you might take to learn more. Maybe you decide to log your data to …","permalink":"/posts/make-a-prediction/","summary":"Imagine you\u0026rsquo;re debugging, and you\u0026rsquo;re stuck. I have a technique that\u0026rsquo;s going to help. Think of an action you might take. Predict what will happen when you take that action. Take the …","title":"Make a Prediction"},{"content":"We\u0026amp;rsquo;ve all seen a desire path. They are footpaths created by erosion from human and animal traffic that communicate a wish for a path that doesn\u0026amp;rsquo;t exist. When you walk on a paved path toward a destination and notice a shortcut in the earth, that\u0026amp;rsquo;s a desire path. It\u0026amp;rsquo;s the way people go, rather than the way we would wish them to go.\nI once read about an architect who omitted footpaths in a college campus design. They built and opened the campus without them. Then, they waited for a year for the desire paths to appear. When they did, the architect paved those paths, …","permalink":"/posts/desire-paths/","summary":"We\u0026rsquo;ve all seen a desire path. They are footpaths created by erosion from human and animal traffic that communicate a wish for a path that doesn\u0026rsquo;t exist. When you walk on a paved path …","title":"Desire Paths in Software"},{"content":"Ben Kuhn\u0026amp;rsquo;s \u0026amp;lsquo;Think Real Hard\u0026amp;rsquo; shares a problem-solving checklist from the scientist Richard Feynman:\nWrite down the problem. Think real hard. Write down the solution. On its face, this advice is ridiculous. If only we just sat and thought about the problem, we\u0026amp;rsquo;d win a Nobel Prize like Dr. Feynman!\nBut what can we learn from this idea? In this post, I\u0026amp;rsquo;d like to explain how I\u0026amp;rsquo;ve used this advice to solve the hard problems in my work.\nThinking Hard I sometimes get stuck. I\u0026amp;rsquo;m making no progress, facing an un-Googleable problem, one nobody on my team knows …","permalink":"/posts/think-hard/","summary":"Ben Kuhn\u0026rsquo;s \u0026lsquo;Think Real Hard\u0026rsquo; shares a problem-solving checklist from the scientist Richard Feynman:\nWrite down the problem. Think real hard. Write down the solution. On its face, …","title":"Spend More Time Thinking Hard"},{"content":"Scenario: you need a database record that can belong to one record or another, but not both. Polymorphism and exclusivity. One approach is to create a polymorphic-style association at the data layer. By doing so, you\u0026amp;rsquo;ll get data integrity built in, rather than trusting it will be enforced by each tenant at the application layer.\nHere\u0026amp;rsquo;s how I\u0026amp;rsquo;ve solved this problem in the past.\nThe Database Migration This is a PostgreSQL example, but it could be applied to other databases.\nConsider a table called packages. Each package could belong to a factory or a store via a factory_id or …","permalink":"/posts/postgresql-polymorphism/","summary":"Scenario: you need a database record that can belong to one record or another, but not both. Polymorphism and exclusivity. One approach is to create a polymorphic-style association at the data layer. …","title":"PostgreSQL Polymorphism"},{"content":"A lot of people start programming with shared dotfiles, copied from a team or online. I did. Maybe you\u0026amp;rsquo;re ready to move on; how would you do that?\nRecently after 6.5 years, I removed the Hashrocket Dotmatrix, a powerful set of tools, from my workstations. In this post, I\u0026amp;rsquo;ll discuss these tools, and why and how I uninstalled them.\nWhat is the Dotmatrix? The Hashrocket Dotmatrix is a set of configurations built by generations of consultants at my previous employer, Hashrocket.\nThe Dotmatrix is a refined, well-maintained, opinionated set of configurations installed on each Hashrocket …","permalink":"/posts/goodbye-shared-dotfiles/","summary":"A lot of people start programming with shared dotfiles, copied from a team or online. I did. Maybe you\u0026rsquo;re ready to move on; how would you do that?\n","title":"Goodbye Shared Dotfiles"},{"content":"You\u0026amp;rsquo;re creating a React app, and want to organize your components. Or maybe you\u0026amp;rsquo;re working in a legacy codebase, with many components in one directory, and you want to better organize them. In this post, I\u0026amp;rsquo;ll document an approach to this problem that has worked for me.\nBesides React, I\u0026amp;rsquo;ve used this in Vue, Nuxt, and Gatbsy applications. Any JavaScript framework that\u0026amp;rsquo;s loosely opinionated about directory structures could receive this structure.\nThe idea: treat components like modules and organize them by route.\nSo, if you have a dashboard page, the dashboard index …","permalink":"/posts/organizing-react-components/","summary":"You\u0026rsquo;re creating a React app, and want to organize your components. Or maybe you\u0026rsquo;re working in a legacy codebase, with many components in one directory, and you want to better organize …","title":"Organizing React Components"},{"content":"I used to have a bad habit when working alone: I\u0026amp;rsquo;d start a feature, begin doubting my approach, throw away my work, and start over from scratch. Sometimes more than once. The result? Wasted energy, abandoned code, confusion about what I had and hadn\u0026amp;rsquo;t implemented, and repetitive rework. This post is a collection of thoughts on this practice.\nThey are:\nIt\u0026amp;rsquo;s okay to start over, for the right reasons Prioritize getting it working Save your work When getting help, don\u0026amp;rsquo;t show up empty-handed It\u0026amp;rsquo;s Okay to Start Over Code Retreats is a programming community that …","permalink":"/posts/on-starting-over/","summary":"I used to have a bad habit when working alone: I\u0026rsquo;d start a feature, begin doubting my approach, throw away my work, and start over from scratch. Sometimes more than once. The result? Wasted …","title":"On Starting Over"},{"content":"Some code is broken, and you can\u0026amp;rsquo;t figure out why. Maybe there are a lot of changes, and identifying the breaking change seems impossible. Or, maybe you\u0026amp;rsquo;re curious about how things break in your organization. The tool you need is git-bisect.\nGit bisect conducts a binary search through your Git history to find a commit of interest. In this post, I\u0026amp;rsquo;ll show how it works.\nA Caveat: Atomic Commits Required For bisect to be useful, you must be committing atomically. If your practice is to squash-merge PRs, or commit swaths of unrelated code with each commit, a bisect may only tell …","permalink":"/posts/find-the-breaking-git-commit/","summary":"Some code is broken, and you can\u0026rsquo;t figure out why. Maybe there are a lot of changes, and identifying the breaking change seems impossible. Or, maybe you\u0026rsquo;re curious about how things break …","title":"How to Identify the Breaking Commit With Git Bisect"},{"content":"You\u0026amp;rsquo;ve been working on a big set of changes, and haven\u0026amp;rsquo;t committed to Git yet. Now, you want to commit some, but not all, changes to a file. Let\u0026amp;rsquo;s look at adding patches.\nHere\u0026amp;rsquo;s the command.\n$ git add --patch --patch lets you interactively choose which changes to apply to a commit.\nApplying Patches Run this command and you\u0026amp;rsquo;ll see the following:\n$ git add --patch diff --git a/README.md b/README.md index 18d277a..b012031 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # [jakeworth][production-url] +A blog of awesome Git tricks. + (1/2) Stage this hunk …","permalink":"/posts/commit-part-of-a-file-in-git/","summary":"You\u0026rsquo;ve been working on a big set of changes, and haven\u0026rsquo;t committed to Git yet. Now, you want to commit some, but not all, changes to a file. Let\u0026rsquo;s look at adding patches.\n","title":"Commit Part of a File in Git"},{"content":"Great programmers aren\u0026amp;rsquo;t fast because they\u0026amp;rsquo;re great. They\u0026amp;rsquo;re great because they\u0026amp;rsquo;re fast.\nI\u0026amp;rsquo;ve sunk a lot of time into being fast at Vim. I\u0026amp;rsquo;ve used it as my text editor for eight years, co-organized a Vim Meetup, and I\u0026amp;rsquo;ve written about Vim on this blog and Hashrocket TIL dozens of times. Why? Because I want to be a fast programmer.\nIn this post, I\u0026amp;rsquo;ll explain why I think being fast at programming is a worthy goal.\nBut what is fast? Ben Orenstein once said that the ideal of text editing should be zero friction between your thought and code on the …","permalink":"/posts/be-fast/","summary":"Great programmers aren\u0026rsquo;t fast because they\u0026rsquo;re great. They\u0026rsquo;re great because they\u0026rsquo;re fast.\n","title":"You Have to Get Fast to Get Good at Programming"},{"content":"A programming style I try to practice could be described as: \u0026amp;ldquo;there should be only one way out of a function.\u0026amp;rdquo; Early returns can often cause more confusion than they\u0026amp;rsquo;re worth. When possible, I avoid them in favor of a single return.\nHere\u0026amp;rsquo;s a demonstration of this style.\nCommon Code: Early Returns Imagine a function that takes as an argument an HTTP response object responding to .ok and returns a message string. Here\u0026amp;rsquo;s an implementation, with an early return, when response.ok is truthy:\nfunction flashMessage(response) { if (response.ok) { return \u0026amp;#39;It …","permalink":"/posts/one-way-out-of-a-function/","summary":"A programming style I try to practice could be described as: \u0026ldquo;there should be only one way out of a function.\u0026rdquo; Early returns can often cause more confusion than they\u0026rsquo;re worth. When …","title":"Only One Way Out of a Function"},{"content":" \u0026amp;ldquo;The best time to plant a tree was twenty years ago. The second best time is now.\u0026amp;rdquo; \u0026amp;ndash; Chinese proverb\nI mentor adults who are learning to program after serving in the military. Some are in their late twenties, and some are twice that age. A common concern is that they are too old to be changing careers to programming.\nAs a career-change programmer, I understand. I discovered programming around age thirty, with no experience and one introductory CS college credit to my name. I had the same concern.\nLet\u0026amp;rsquo;s distinguish this question from a different question: \u0026amp;ldquo;am I …","permalink":"/posts/am-i-too-old-to-become-a-programmer/","summary":" \u0026ldquo;The best time to plant a tree was twenty years ago. The second best time is now.\u0026rdquo; \u0026ndash; Chinese proverb\nI mentor adults who are learning to program after serving in the military. Some …","title":"Am I Too Old to Become a Programmer?"},{"content":"In Things You Should Never Do, Part I, Joel Spolsky narrates Netscape\u0026amp;rsquo;s ruinous decision to rewrite their browser from scratch. This introduced the following concept to me: \u0026amp;ldquo;It\u0026amp;rsquo;s harder to read code than to write it.\u0026amp;rdquo; I believe this is true. Today I\u0026amp;rsquo;d like to explain why.\nA Practical Example As an example, consider the \u0026amp;ldquo;liking\u0026amp;rdquo; feature of Today I Learned. This is an icon under each Today I Learned post that you can click to indicate: \u0026amp;ldquo;I like this post.\u0026amp;rdquo;\nWhen we explore TIL\u0026amp;rsquo;s codebase, we see this method called when a visitor clicks …","permalink":"/posts/its-harder-to-read-code-than-write-it/","summary":"In Things You Should Never Do, Part I, Joel Spolsky narrates Netscape\u0026rsquo;s ruinous decision to rewrite their browser from scratch. This introduced the following concept to me: \u0026ldquo;It\u0026rsquo;s …","title":"It's Harder to Read Code Than Write It"},{"content":"Screenshots of code are common in developer communication, but they come with real drawbacks. Here\u0026amp;rsquo;s why I try to avoid them.\nScreenshots happen. People are often under time pressure, on phones, or unfamiliar with best practices. Some people feel more confident communicating with images. I\u0026amp;rsquo;m here to offer solutions, not shame.\nProblem Background Why does one post screenshots? To be helpful. You\u0026amp;rsquo;re stuck and want to provide high-bandwidth information. What could be higher bandwidth than a picture? I appreciate that helpful spirit.\nBut code screenshots have limitations:\nThey …","permalink":"/posts/on-code-screenshots/","summary":"Screenshots of code are common in developer communication, but they come with real drawbacks. Here\u0026rsquo;s why I try to avoid them.\n","title":"The Problems With Code Screenshots"},{"content":"Conditional logic has its place, but often there\u0026amp;rsquo;s a better alternative. Today, we\u0026amp;rsquo;ll look at a Ruby solution: a hash with .fetch.\nMy thanks to Brian Dunn, who showed me the value of this technique.\nImagine you have a method that translates some data, a symbol for a role, into a presentation. A conditional is a popular solution here:\ndef display_name(role) if role == :customer \u0026amp;#39;angler\u0026amp;#39; elsif role == :employee \u0026amp;#39;guide\u0026amp;#39; else name.to_s end end I think this method is much improved as a hash:\nROLE_MAP = { customer: \u0026amp;#39;angler\u0026amp;#39;, employee: \u0026amp;#39;guide\u0026amp;#39; } def …","permalink":"/posts/hash-fetch-instead-of-if-else/","summary":"Conditional logic has its place, but often there\u0026rsquo;s a better alternative. Today, we\u0026rsquo;ll look at a Ruby solution: a hash with .fetch.\n","title":"Hash Fetch Instead of If/Else"},{"content":"I recently completed a winter survival course where we built shelters in just ten minutes with only the contents of our packs. The pack I brought was nearly empty, so I made a tent out of my parka. It was ugly, but it could have saved my life. How does this apply to software? When building a feature, first get it working, then make it look good.\nLet\u0026amp;rsquo;s say you\u0026amp;rsquo;re building an app that queries a third-party API to get data. \u0026amp;ldquo;Get it working\u0026amp;rdquo; could mean getting the API to return the data you need, and then dumping it onto the page as a raw object with JSON.stringify. It …","permalink":"/posts/get-it-working-first/","summary":"I recently completed a winter survival course where we built shelters in just ten minutes with only the contents of our packs. The pack I brought was nearly empty, so I made a tent out of my parka. It …","title":"First Get It Working, Then Make it Look Good"},{"content":"RC files, thought to stand for \u0026amp;ldquo;run command\u0026amp;rdquo; files, are read by a program during startup. The file that configures the PostgreSQL REPL psql is called .psqlrc. In this post I\u0026amp;rsquo;ll share my personal psql configuration.\nNote: many of these configurations came from my time at Hashrocket. We documented some these preferences via PG Casts.\nHere\u0026amp;rsquo;s my .psqlrc.\n\\set QUIET on \\pset null \u0026amp;#39;ø\u0026amp;#39; \\x auto \\timing \\set HISTFILE ~/.psql_history-:DBNAME \\set HISTCONTROL ignoredups \\set QUIET off Let\u0026amp;rsquo;s look at each setting.\nSilence Startup I wrap all the commands in a …","permalink":"/posts/my-psql-config/","summary":"RC files, thought to stand for \u0026ldquo;run command\u0026rdquo; files, are read by a program during startup. The file that configures the PostgreSQL REPL psql is called .psqlrc. In this post I\u0026rsquo;ll share …","title":"My psql Config"},{"content":"How does one exit Vim \u0026amp;ldquo;Ex\u0026amp;rdquo; mode? But first\u0026amp;hellip; how did you even get into \u0026amp;ldquo;Ex\u0026amp;rdquo; mode?\nProbably by accidentally typing Q in normal mode:\n*Q* *mode-Ex* *Ex-mode* *Ex* *EX* *E501* Q\tSwitch to \u0026amp;#34;Ex\u0026amp;#34; mode. This is a bit like typing \u0026amp;#34;:\u0026amp;#34; commands one after another, except: - You don\u0026amp;#39;t have to keep pressing \u0026amp;#34;:\u0026amp;#34;. - The screen doesn\u0026amp;#39;t get updated after each command. - There is no normal command-line editing. - Mappings and abbreviations are not used. If this is true you probably missed the initial instructions:\nEntering Ex mode. Type …","permalink":"/posts/exit-vim-ex-mode/","summary":"How does one exit Vim \u0026ldquo;Ex\u0026rdquo; mode? But first\u0026hellip; how did you even get into \u0026ldquo;Ex\u0026rdquo; mode?\n","title":"Solved! How to Exit Vim \"Ex\" Mode"},{"content":"A stumbling block for many people when debugging is reading the stack trace. Today I\u0026amp;rsquo;d like to discuss this important skill.\nWhat is a Stack Trace? What is a stack trace? According to Wikipedia: \u0026amp;ldquo;In computing, a stack trace\u0026amp;hellip; is a report of the active stack frames at a certain point in time during the execution of a program.\u0026amp;rdquo; It\u0026amp;rsquo;s the answer to the question: \u0026amp;ldquo;when this system is being observed, what is happening?\u0026amp;rdquo; Reading stack traces is a core programming skill.\nI compare reading stack traces to troubleshooting a component of a car. Imagine that you …","permalink":"/posts/how-to-read-a-stack-trace/","summary":"A stumbling block for many people when debugging is reading the stack trace. Today I\u0026rsquo;d like to discuss this important skill.\n","title":"How to Read a Stack Trace"},{"content":"Ruby\u0026amp;rsquo;s RSpec describe block has two common syntaxes. Which should you use?\n# With `RSpec` module name RSpec.describe TestedClass do end # Without module name describe TestedClass do end Disclaimer: they both work. But I\u0026amp;rsquo;m assuming since you\u0026amp;rsquo;re here, you\u0026amp;rsquo;re curious about the distinction, or want to make a reasoned choice.\nTL;DR: I suggest using the RSpec module name.\nThe Options The global availability of describe is called the Global Namespace DSL. RSpec made describe available on Object, the default root of all Ruby objects. The intent was to let us write specs without …","permalink":"/posts/rspec-describe-vs-describe/","summary":"Ruby\u0026rsquo;s RSpec describe block has two common syntaxes. Which should you use?\n","title":"RSpec.describe vs. describe: Which Should I Use?"},{"content":"When building a demo application, I believe you can often skip authentication.\nPicture this: a coding school student team demoing a project. Smart and creative folks with lots to offer. Yet they spend half of their limited building and demoing time on auth. Here\u0026amp;rsquo;s a sample script from the demo:\n\u0026amp;ldquo;So we click \u0026amp;rsquo;login\u0026amp;rsquo; and now we\u0026amp;rsquo;re on the login page, which we know because the header says \u0026amp;lsquo;Log in.\u0026amp;rsquo; Then, we can either log in, sign in, or reset our password. We\u0026amp;rsquo;re going to log in with a username and password I set up before the demo. I just fill in …","permalink":"/posts/your-demo-doesnt-need-auth/","summary":"When building a demo application, I believe you can often skip authentication.\n","title":"Your Demo Doesn't Need Auth"},{"content":"I remember the first bug that I shipped to production. I was upset that I\u0026amp;rsquo;d broken something and was anxious to fix it. But I noticed something curious: the calm demeanor of a senior mentor helping me. They refused to meet my intensity. While the world burned, they wanted to instead discuss the bug and its relative importance.\nEngineers don\u0026amp;rsquo;t want to break things. We don\u0026amp;rsquo;t want to make mistakes or look dumb. We don\u0026amp;rsquo;t want to ship a sup-par experience or disappoint customers. When I was a junior engineer I reacted strongly to bugs that I caused or contributed to, no …","permalink":"/posts/the-importance-of-a-bug/","summary":"I remember the first bug that I shipped to production. I was upset that I\u0026rsquo;d broken something and was anxious to fix it. But I noticed something curious: the calm demeanor of a senior mentor …","title":"How Urgent Is This Bug?"},{"content":"You were stuck, and now you aren\u0026amp;rsquo;t. Congratulations! Before you move on, it\u0026amp;rsquo;s vital to stop and learn from it. It\u0026amp;rsquo;s the best way I know to get better and spare your mind for increasingly harder problems.\n\u0026amp;ldquo;What Did I Miss?\u0026amp;rdquo; Every time you get unstuck, ask yourself: when I was stuck, what did I miss?\nWas I misreading a message? Was I hypnotized by an irrelevant detail? 😵‍💫 Did I make a bad assumption and reject information that didn\u0026amp;rsquo;t fit into my assumption? \u0026amp;ldquo;What did I miss?\u0026amp;rdquo;\nIf you received help from someone, think back to the last question they …","permalink":"/posts/learn-from-it/","summary":"You were stuck, and now you aren\u0026rsquo;t. Congratulations! Before you move on, it\u0026rsquo;s vital to stop and learn from it. It\u0026rsquo;s the best way I know to get better and spare your mind for …","title":"Debugging Tip: Learning From Bugs"},{"content":"Names in software are hard. But what\u0026amp;rsquo;s worse than a bad name? Sticking with it because you can\u0026amp;rsquo;t change it. I want us all to be able to effortlessly and fearlessly fix bad names. That\u0026amp;rsquo;s the topic of this post.\nConsidering a \u0026amp;ldquo;Bad\u0026amp;rdquo; Name What\u0026amp;rsquo;s a bad name? Here\u0026amp;rsquo;s a contrived, but not production-unrealistic, example:\nfunction longName() { if(useLongName) { return `${this.firstName} ${this.lastName}` } else { return this.firstName[0] } } A few issues I can raise with this name:\nlongName is often not an accurate description of what this function does. If …","permalink":"/posts/make-renames-easy/","summary":"Names in software are hard. But what\u0026rsquo;s worse than a bad name? Sticking with it because you can\u0026rsquo;t change it. I want us all to be able to effortlessly and fearlessly fix bad names. …","title":"How to Make Renames Easy"},{"content":"Have you ever tried to read the documentation for a Postgres command? Although many consider Postgres\u0026amp;rsquo; docs best-in-class, they include conventions that might challenge newbies. In this post, I\u0026amp;rsquo;ll paraphrase Postgres\u0026amp;rsquo; conventions page to help us understand what the documentation for a command is saying.\nTo check the syntax of a command, I start with psql, the REPL, or \u0026amp;lsquo;Read-Evaluate-Print-Loop\u0026amp;rsquo;, of Postgres. I query using \\help , and get output like this for update:\njakes_db=# \\help update Command: UPDATE Description: update rows of a table Syntax: [ WITH [ …","permalink":"/posts/how-to-read-postgres-docs/","summary":"Have you ever tried to read the documentation for a Postgres command? Although many consider Postgres\u0026rsquo; docs best-in-class, they include conventions that might challenge newbies. In this post, …","title":"How to Read Postgres Docs"},{"content":"Something I took for granted at Hashrocket was the file that configures Exhuberant Ctags. Without configuration, recursive tag generation will tag many things and cause problems. overindex badly.\nHere\u0026amp;rsquo;s how you configure Ctags to ignore things:\n/ctags.cnf (on MSDOS, MSWindows only) /etc/ctags.conf /usr/local/etc/ctags.conf $HOME/.ctags $HOME/ctags.cnf (on MSDOS, MSWindows only) .ctags ctags.cnf (on MSDOS, MSWindows only) If any of these configuration files exist, each will be expected to contain a set of default options which are read in the order listed when ctags starts, but before the …","permalink":"/tils/ctags-dotfiles/","summary":"Something I took for granted at Hashrocket was the file that configures Exhuberant Ctags. Without configuration, recursive tag generation will tag many things and cause problems. overindex badly. …","title":"Ctags Dotfiles"},{"content":"If I could give one piece of advice to anyone learning to program: build.\nThis advice might be most helpful to beginners. But it\u0026amp;rsquo;s been valuable to me to revisit at different points in my career. In some ways, this post is for me.\nSteve Martin once said the secret to success is to \u0026amp;ldquo;Be so good they can\u0026amp;rsquo;t ignore you.\u0026amp;rdquo; How do you get that good as a programmer? The most direct path I know is to build things.\nIt\u0026amp;rsquo;s okay to do a tutorial, watch a few videos, read the official docs. These are training wheels and you want to take them off as soon as possible.\nPrioritizing …","permalink":"/posts/build/","summary":"If I could give one piece of advice to anyone learning to program: build.\n","title":"Stop Studying, Start Building"},{"content":"Which operating system is the best for programming? This is a perennial question in code boot camps where I\u0026amp;rsquo;ve taught. I think students see the choice as a fork in the road, and they want pick the right direction. I don\u0026amp;rsquo;t think there is a right direction. When you\u0026amp;rsquo;re starting out, my advice is to try each one and choose for yourself.\nDon\u0026amp;rsquo;t be this person:\n\u0026amp;ldquo;I hate Linux! Full disclosure, I\u0026amp;rsquo;ve only ever programmed on Windows\u0026amp;hellip;\u0026amp;rdquo;\nThe operating system you choose does matter, because programs run differently on each OS. Look beyond the marketing pages …","permalink":"/posts/try-every-os/","summary":"Which operating system is the best for programming? This is a perennial question in code boot camps where I\u0026rsquo;ve taught. I think students see the choice as a fork in the road, and they want pick …","title":"Which Operating System Is the Best for Programming?"},{"content":"Auto-formatting code is good, and you should be doing it all the time. Why? It helps you write better code in real-time, preempt trivial discussions, and maybe even democratize programming.\nBetter Code in Real-time First, writing better code in real-time: I\u0026amp;rsquo;ll use the Prettier auto-formatter as an example. I have Prettier running everywhere, auto-formatting on write. This means that each time I write my file, which I do a lot, Prettier attempts to format my code. Neatening my work as I go along helps me think clearly and see refactoring opportunities.\nThere\u0026amp;rsquo;s an amazing …","permalink":"/posts/autoformat-your-code/","summary":"Auto-formatting code is good, and you should be doing it all the time. Why? It helps you write better code in real-time, preempt trivial discussions, and maybe even democratize programming.\n","title":"Autoformat Your Code"},{"content":"Code reviews are important on many teams. Do them well, and your code ships quickly and safely. Do them poorly, and your code ships slowly and riskily. I try to contribute good code reviews. In this post, I\u0026amp;rsquo;ll share my process.\nWhat I Bring First, I try to be nice 😊. There\u0026amp;rsquo;s a lot of writing about how internet comments can be perceived as less friendly than the author intended, and code reviews suffer from this. The comments are visible to your team, too, so the emotional stakes can be high. Approaching the work with curiosity, saying \u0026amp;ldquo;thank you\u0026amp;rdquo;, and adding emojis and …","permalink":"/posts/how-i-review-code/","summary":"Code reviews are important on many teams. Do them well, and your code ships quickly and safely. Do them poorly, and your code ships slowly and riskily. I try to contribute good code reviews. In this …","title":"How I Review Code"},{"content":"I think the most important factor in consistent delivery is understanding the work. When you understand the work, you build what the stakeholder wants, better and faster.\nHere\u0026amp;rsquo;s an idea that\u0026amp;rsquo;s not ready for programming work: \u0026amp;ldquo;Let\u0026amp;rsquo;s add social login to our website.\u0026amp;rdquo; Same idea, ready for work: \u0026amp;ldquo;A logged-out customer sees the Google login button,\u0026amp;rdquo; with clear acceptance criteria about what that means.\nIn this post, I\u0026amp;rsquo;ll explain how I go from such a vague idea to a deliverable unit of work.\nWhat\u0026amp;rsquo;s the \u0026amp;lsquo;Why\u0026amp;rsquo;? It starts with a …","permalink":"/posts/how-i-make-sure-i-understand-a-feature-before-building/","summary":"I think the most important factor in consistent delivery is understanding the work. When you understand the work, you build what the stakeholder wants, better and faster.\n","title":"How I Make Sure I Understand a Feature Before Building"},{"content":"We\u0026amp;rsquo;ve all seen this: a frustrated coworker hunched over a computer after hours, flailing alone against some impossible bug. Go home, coworker. Don\u0026amp;rsquo;t stay stuck.\nI once took a CS course that featured an online puzzle where you had to perform a series of tasks in a seemingly random order to solve it. The solution was not discoverable without brute force. You just tried each step until something happened, and then you tried another. It was maddening, and I gave up.\nThe course presented this as a lesson in resilience. Sticking with a problem without clear direction. I learned that, …","permalink":"/posts/dont-stay-stuck/","summary":"We\u0026rsquo;ve all seen this: a frustrated coworker hunched over a computer after hours, flailing alone against some impossible bug. Go home, coworker. Don\u0026rsquo;t stay stuck.\n","title":"Don't Stay Stuck"},{"content":"I\u0026amp;rsquo;ve been using, teaching, and stanning Vim since almost the beginning of my programming career. Yet, when asked to explain this preference, I stumble. In this post, I\u0026amp;rsquo;d like to explore why I love Vim.\nDisclaimer: I haven\u0026amp;rsquo;t used every text editor that has ever existed! I haven\u0026amp;rsquo;t used Emacs or some of the GUI editors that have come and gone over the years. I\u0026amp;rsquo;m not arguing that Vim is better than any of these. In particular, VSCode in Vim mode seems to rival Vim in some ways.\nSo, why Vim?\nWhy Vim I love the terminal basis. Yes, this argument applies to any …","permalink":"/posts/why-vim/","summary":"I\u0026rsquo;ve been using, teaching, and stanning Vim since almost the beginning of my programming career. Yet, when asked to explain this preference, I stumble. In this post, I\u0026rsquo;d like to explore …","title":"Why Vim"},{"content":"Today I want to talk about a common technique: disabling failing tests to allow a feature to ship. Maybe sometimes you gotta do it. But long-term I think it causes more problems than it solves.\nHere\u0026amp;rsquo;s the scenario: we\u0026amp;rsquo;re shipping an important hotfix, but there\u0026amp;rsquo;s one red test blocking a green suite, and thus our deploy. This test is flaky\u0026amp;ndash; it\u0026amp;rsquo;s known to pass and fail on the same commit. Folks on our team are suggesting disabling it. Wanting to be a team player, we disable the test with a short commit, \u0026amp;ldquo;Disable this test for now\u0026amp;rdquo; and ship our code.\nWhat …","permalink":"/posts/on-disabling-tests/","summary":"Today I want to talk about a common technique: disabling failing tests to allow a feature to ship. Maybe sometimes you gotta do it. But long-term I think it causes more problems than it solves.\n","title":"On Disabling Tests"},{"content":"Here\u0026amp;rsquo;s a trick that that has helped me as a programmer: before doing anything major, like killing a process, stop and count to ten.\nI learned this in IT. When a screen froze, the IT professionals I worked with would stop and count to ten before taking action.\nYou\u0026amp;rsquo;re watching a process run, it seems to have frozen, and you\u0026amp;rsquo;re getting no instructions. Before killing it, stop and count to ten. If the process is still hanging after that, kill it, if you must.\nComputers don\u0026amp;rsquo;t operate on human time, and our brains are ill-equipped to conceive of what they\u0026amp;rsquo;re doing and …","permalink":"/posts/count-to-ten/","summary":"Here\u0026rsquo;s a trick that that has helped me as a programmer: before doing anything major, like killing a process, stop and count to ten.\n","title":"Before You Abort, Count to Ten"},{"content":"A common, problematic convention I see in Ruby tests are variable names like this:\nuser_a = create(:user, last_log_in: today) user_b = create(:user, last_log_in: last_year) The idea is \u0026amp;ldquo;we need two users to compare behavior against, so let\u0026amp;rsquo;s make User A and User B.\u0026amp;rdquo; In this post, I\u0026amp;rsquo;d like to argue for variable names like this:\nfrequent_user = create(:user, last_log_in: today) unengaged_user = create(:user, last_log_in: last_year) frequent and unengaged are not literal suggestion, but rather representatives of an idea. That idea is that such variables are visually …","permalink":"/posts/avoid-similar-variable-names/","summary":"A common, problematic convention I see in Ruby tests are variable names like this:\nuser_a = create(:user, last_log_in: today) user_b = create(:user, last_log_in: last_year) ","title":"Avoid Similar Variable Names"},{"content":"Any command you type out manually, or even tab-complete a few times, can be shortened. A common shortening technique is the terminal alias. Here are some tips that help me write better aliases and cut my terminal keystrokes.\nIdentify Alias Opportunities With the \u0026amp;rsquo;typeless\u0026amp;rsquo; Command I found this trick a long time ago from, I believe, Thoughbot\u0026amp;rsquo;s Upcase course. I call it typeless, and it\u0026amp;rsquo;s a well-worn alias in my .zshrc:\n# ~/.zshrc # Find commands I type often so I can alias them alias typeless=\u0026amp;#39;history n 20000 | sed \u0026amp;#34;s/.* //\u0026amp;#34; | sort | uniq -c | sort -g | tail …","permalink":"/posts/alias-terminal-commands/","summary":"Any command you type out manually, or even tab-complete a few times, can be shortened. A common shortening technique is the terminal alias. Here are some tips that help me write better aliases and cut …","title":"Refining Your Terminal Aliases"},{"content":"Open a production Ruby file, and you\u0026amp;rsquo;ll often see this magic comment at the top.\n# frozen_string_literal: true Today I\u0026amp;rsquo;d like to argue that most Ruby files do not need this comment. YAGNI— You Aren\u0026amp;rsquo;t Going to Need It.\nWhy? I believe it:\nIs a relic of the past Doesn\u0026amp;rsquo;t do what people think Isn\u0026amp;rsquo;t Ruby-ish Is associated with unproven performance claims I\u0026amp;rsquo;d like to see it deleted from the code I work on, and I don\u0026amp;rsquo;t think we should be enforcing its presence most of the time.\nSo, what is a magic comment? Here\u0026amp;rsquo;s an explanation from the Ruby docs:\nWhile …","permalink":"/posts/frozen-string-comment-yagni/","summary":"Open a production Ruby file, and you\u0026rsquo;ll often see this magic comment at the top.\n# frozen_string_literal: true Today I\u0026rsquo;d like to argue that most Ruby files do not need this comment. YAGNI— …","title":"Ruby's Frozen String Literal Comment: YAGNI"},{"content":"When I create Agile bug tickets, I leave the story points blank. Why? Two reasons: pointing bugs creates the wrong incentives, and bugs are hard to estimate.\nBug Terminology First, some context.\nPointing is an Agile process of estimating how long a piece of work will take, or how complex it is. Bugs are defects in a computer program that produce undesirable behavior. And so, pointing bugs means trying to estimate how long it will take to fix a bug, or how complex it is.\nSoftware engineering managers don\u0026amp;rsquo;t like bugs, and like knowing when they will be fixed. Pointing the bugs would seem a …","permalink":"/posts/why-i-dont-point-bugs/","summary":"When I create Agile bug tickets, I leave the story points blank. Why? Two reasons: pointing bugs creates the wrong incentives, and bugs are hard to estimate.\n","title":"Why I Don't Point Agile Bug Tickets"},{"content":"\u0026amp;ldquo;What did you do this week?\u0026amp;rdquo; I\u0026amp;rsquo;ve written a weekly summary for myself and my teams for years. It helps me think through my work and shows people what I\u0026amp;rsquo;m working on. In this post, I\u0026amp;rsquo;ll explain how to use this tool.\nWhy write a summary Why write a weekly summary? Two reasons: it helps me think through my work, and shows people what I\u0026amp;rsquo;m working on.\nTaking thirty minutes to pause, reflect, and plan helps me sharpen my thinking and transition from week-to-week. Many developers keep a rubber duck on their desk for this reason. Rubber-ducking lets you talk through …","permalink":"/posts/weekly-summary-routine/","summary":"\u0026ldquo;What did you do this week?\u0026rdquo; I\u0026rsquo;ve written a weekly summary for myself and my teams for years. It helps me think through my work and shows people what I\u0026rsquo;m working on. In this …","title":"Weekly Summary Technique for Software Engineers"},{"content":"Here\u0026amp;rsquo;s a technique I\u0026amp;rsquo;ve used on launches: instead of a big scary waterfall, or, in tandem with a phased release, let customers opt into a new feature.\nOn the first step of your legacy app, add a link to the new app that says \u0026amp;ldquo;Try our new beta app!\u0026amp;rdquo; Then, add add a link to the new app that says \u0026amp;ldquo;Go back to the legacy app\u0026amp;rdquo;, and track clicks to it.\nThe benefit is that some early adopters will try the new app voluntarily, and their tolerance for issues will be high because they\u0026amp;rsquo;ll think \u0026amp;ldquo;I chose to opt into this and it\u0026amp;rsquo;s a beta feature.\u0026amp;rdquo; …","permalink":"/posts/opt-into-beta/","summary":"Here\u0026rsquo;s a technique I\u0026rsquo;ve used on launches: instead of a big scary waterfall, or, in tandem with a phased release, let customers opt into a new feature.\n","title":"Opt Into Beta"},{"content":"I have collected a group of mentors over the years. I call on them frequently to talk through tough problems. I think I\u0026amp;rsquo;m better at my job because of this regular, impartial outside feedback. I\u0026amp;rsquo;d wish this for everyone! In this post, I\u0026amp;rsquo;ll try to reverse-engineer the path I took to get here.\nI think it comes down to three things: finding smart people, being mentorable, and giving back.\nFind Smart People First, you have to find great programmers to learn from.\nIn Consultancy Cycles, I talked about how great programmers can often be found at a consultancy. You can also find them …","permalink":"/posts/finding-a-mentor/","summary":"I have collected a group of mentors over the years. I call on them frequently to talk through tough problems. I think I\u0026rsquo;m better at my job because of this regular, impartial outside feedback. …","title":"How to Find a Programming Mentor"},{"content":"An engineering technique I recommend: when you sign up for any service, even on a side project that\u0026amp;rsquo;s just getting off the ground, create an email group called dev@\u0026amp;lt;your-domain.com\u0026amp;gt; that forwards to you and any other technologists, and use that email to sign up.\nI\u0026amp;rsquo;ve worked on dozens of projects where the founding programmer signed up for every conceivable infrastructure service\u0026amp;ndash; think AWS, Heroku, Google, Facebook, etc.\u0026amp;ndash; with their personal email or individual work email. The problems this causes are many.\nEven if that programmer never leaves, it robs the team of …","permalink":"/posts/use-a-dev-email/","summary":"An engineering technique I recommend: when you sign up for any service, even on a side project that\u0026rsquo;s just getting off the ground, create an email group called dev@\u0026lt;your-domain.com\u0026gt; that …","title":"Use a Dev Email"},{"content":"Here\u0026amp;rsquo;s my simple framework for answering questions on Stack Overflow that reach and help people and earn reputation.\nI answer questions by:\nFinding the question Gathering information Answering quickly Making comments obsolete As a working example, here\u0026amp;rsquo;s an accepted answer of mine: \u0026amp;lsquo;Converting React files to Typescript\u0026amp;rsquo;. I\u0026amp;rsquo;ll be referencing it throughout.\nWhy Bother Answering at All? But first, let\u0026amp;rsquo;s check in. Why bother answering questions at all? You don\u0026amp;rsquo;t get paid or promoted because of it.\nI can only speak for myself. I enjoy answering questions on …","permalink":"/posts/answering-questions-on-stack-overflow/","summary":"Here\u0026rsquo;s my simple framework for answering questions on Stack Overflow that reach and help people and earn reputation.\n","title":"How to Answer Questions on Stack Overflow"},{"content":"Hard-coded test expectations have many benefits that I\u0026amp;rsquo;ll explore in this post.\nHere\u0026amp;rsquo;s a kind of assertion I see often in tests:\n# Code class User \u0026amp;lt; ApplicationRecord def full_name \u0026amp;#34;#{first_name} #{last_name}\u0026amp;#34; end end # Assertion expect(user.full_name).to \\ eq(\u0026amp;#34;#{user.first_name} #{user.last_name}\u0026amp;#34;) And here\u0026amp;rsquo;s that same assertion with a hard-coded expectation, which is almost always better:\nexpect(user.full_name).to eq(\u0026amp;#34;Jake Worth\u0026amp;#34;) A hard-coded expectation is an assertion about behavior that is just dumbly spelled out. They\u0026amp;rsquo;re better because: …","permalink":"/posts/hard-code-your-expectations/","summary":"Hard-coded test expectations have many benefits that I\u0026rsquo;ll explore in this post.\n","title":"Want Better Automated Tests? Hard-Code Your Expectations"},{"content":"When possible, I prefer to use the presence of data to represent a boolean, rather than a boolean itself.\nExample: Error State Here\u0026amp;rsquo;s a bug vector I see a lot in frontend code. Imagine we\u0026amp;rsquo;ve made a network request that returns an error. Our code sets a boolean errorState, meant to convey \u0026amp;ldquo;we are in an error state\u0026amp;rdquo;, to true. Then we display an errorMessage in that condition:\nif(isErrorState) { displayMessage(errorMessage) } The problem here is that we now have two variables tied to our error condition: isErrorState and errorMessage. And when that happens, it\u0026amp;rsquo;s easy …","permalink":"/posts/presence-is-boolean/","summary":"When possible, I prefer to use the presence of data to represent a boolean, rather than a boolean itself.\n","title":"Presence is Boolean"},{"content":"From normal mode, N% will jump you to that percentage of the file. So 50% jumps to halfway through the file.\n","permalink":"/tils/jump-to-n-percent-of-a-file/","summary":"From normal mode, N% will jump you to that percentage of the file. So 50% jumps to halfway through the file.\n","title":"Jump to N Percent of a File"},{"content":"An idea I find useful as an individual contributor is starting my consulting with this phrase: \u0026amp;ldquo;I think that\u0026amp;hellip;\u0026amp;rdquo;. The idea is to let your arguments stand on their own, rather than appealing to your experience or other authorities.\nI think a mark of a mature engineer is having strong opinions, weakly held, that you can articulate.\nHaving some experience is a conundrum. You know a lot of good and bad ways to do something, but you can\u0026amp;rsquo;t just download your expertise onto someone else. If you don\u0026amp;rsquo;t share it wisely, it\u0026amp;rsquo;s easy to sound pessimistic, preachy, or …","permalink":"/posts/i-think-that/","summary":"An idea I find useful as an individual contributor is starting my consulting with this phrase: \u0026ldquo;I think that\u0026hellip;\u0026rdquo;. The idea is to let your arguments stand on their own, rather than …","title":"Be a Better Technical Consultant by Saying \"I Think That...\""},{"content":" Me: Hey, I\u0026amp;rsquo;m experiencing an issue with your software. Here\u0026amp;rsquo;s a detailed bug report.\nSAAS Engineer: What you\u0026amp;rsquo;re describing isn\u0026amp;rsquo;t possible.\nNarrator: But it was possible.\nThis is a frustrating, unproductive snippet of a conversation I\u0026amp;rsquo;ve had a few times in my career. What can we learn? Treat bug reports like gold.\nFor every person who takes the time to fill out your support form, there are many more who just gave up, found a janky workaround, or possibly abandoned your product altogether. It costs them nothing to move on. You might not like the message, or how it …","permalink":"/posts/bug-reports-are-gold/","summary":" Me: Hey, I\u0026rsquo;m experiencing an issue with your software. Here\u0026rsquo;s a detailed bug report.\nSAAS Engineer: What you\u0026rsquo;re describing isn\u0026rsquo;t possible.\nNarrator: But it was possible.\nThis …","title":"Bug Reports Are Gold"},{"content":"Unused dependencies are bad: they increase the size of your project, slow down processes, require maintenance, and send incorrect messages to fellow developers about what\u0026amp;rsquo;s important.\nTo get find unused dependencies in Ruby, I\u0026amp;rsquo;ve been using gem stale:\ngem stale gem stale list gems along with most recent access times. If the last access time was the day you set up the app, that gem is a candidate for removal.\n","permalink":"/tils/find-stale-gems/","summary":"Unused dependencies are bad: they increase the size of your project, slow down processes, require maintenance, and send incorrect messages to fellow developers about what\u0026rsquo;s important.\nTo get …","title":"Find Stale Gems"},{"content":"The longer I program (ahem), the bigger I want my terminal fonts. I think most programmers should be using a bigger font than they are. Complicating matters, the standard font for iTerm is miniscule on a computer monitor.\nToday I discovered iTerm\u0026amp;rsquo;s \u0026amp;lsquo;View \u0026amp;gt; Size Changes Update Profile\u0026amp;rsquo; feature. When enabled, changes to your font size via ⌘ + and ⌘ - will be applied to your iTerm profile. The next time you open iTerm, the new font size will persist.\n","permalink":"/tils/save-size-changes-to-iterm-profile/","summary":"The longer I program (ahem), the bigger I want my terminal fonts. I think most programmers should be using a bigger font than they are. Complicating matters, the standard font for iTerm is miniscule …","title":"Save Size Change to iTerm Profile"},{"content":"Hit K on a keyword, and Vim looks up the program under the keyword. It\u0026amp;rsquo;s a bit jarring because Vim appears to shell out from your buffer to show the definition.\nTo see that man page in Vim, load the man.vim plugin from command mode or in your .vimrc:\n:runtime! ftplugin/man.vim Now, you can read man pages as a Vim buffer:\n:Man curl ","permalink":"/tils/man-pages-in-vim/","summary":"Hit K on a keyword, and Vim looks up the program under the keyword. It\u0026rsquo;s a bit jarring because Vim appears to shell out from your buffer to show the definition.\nTo see that man page in Vim, load …","title":"Man pages in Vim"},{"content":"PostgreSQL was originally named \u0026amp;ldquo;POSTGRES\u0026amp;rdquo;— all caps. The name was chosen because it was the successor (\u0026amp;ldquo;post-\u0026amp;rdquo;) to the \u0026amp;ldquo;Ingres\u0026amp;rdquo; database. As the system adopted SQL standards and features, the \u0026amp;ldquo;ql\u0026amp;rdquo; as added and capitalization refined.\nFrom the PostgreSQL docs:\nMany people continue to refer to PostgreSQL as \u0026amp;ldquo;Postgres\u0026amp;rdquo; (now rarely in all capital letters) because of tradition or because it is easier to pronounce. This usage is widely accepted as a nickname or alias.\nThe official pronunciation is \u0026amp;ldquo;Post-Gres-Q-L.\u0026amp;rdquo;\nSources: …","permalink":"/tils/history-of-the-postgresql-name/","summary":"PostgreSQL was originally named \u0026ldquo;POSTGRES\u0026rdquo;— all caps. The name was chosen because it was the successor (\u0026ldquo;post-\u0026rdquo;) to the \u0026ldquo;Ingres\u0026rdquo; database. As the system adopted SQL …","title":"Why Is It Called PostgreSQL?"},{"content":"Linters are great, except when they aren\u0026amp;rsquo;t. One example is Rubocop\u0026amp;rsquo;s BlockLength lint. For example, I don\u0026amp;rsquo;t care if my RSpec describe and context blocks are too long. Nontrivial test blocks will never be short enough to match a reasonable rule about blocks.\nI\u0026amp;rsquo;ve disabled this lint for these blocks with Rubocop\u0026amp;rsquo;s configuration file:\n# .rubocop.yml Metrics/BlockLength: IgnoredMethods: [\u0026amp;#39;context\u0026amp;#39;, \u0026amp;#39;describe\u0026amp;#39;] Opting out of just these blocks lets me enforce this rule everywhere else.\n","permalink":"/tils/ignore-rubocop-block-length-lint-in-rspec/","summary":"Linters are great, except when they aren\u0026rsquo;t. One example is Rubocop\u0026rsquo;s BlockLength lint. For example, I don\u0026rsquo;t care if my RSpec describe and context blocks are too long. Nontrivial test …","title":"Ignore Rubocop Block Length Lint in RSpec"},{"content":"Today I learned about the method __id__, aliased object_id, in Ruby. It returns a unique integer identifier for any Ruby object.\nA few examples:\n\u0026amp;gt; Object.new.object_id =\u0026amp;gt; 7024702983434 \u0026amp;gt; name = \u0026amp;#34;jake\u0026amp;#34; =\u0026amp;gt; \u0026amp;#34;jake\u0026amp;#34; \u0026amp;gt; name.object_id == name.object_id =\u0026amp;gt; true \u0026amp;gt; \u0026amp;#34;thawed\u0026amp;#34;.object_id == \u0026amp;#34;thawed\u0026amp;#34;.object_id =\u0026amp;gt; false \u0026amp;gt; \u0026amp;#34;frozen\u0026amp;#34;.freeze.object_id == \u0026amp;#34;frozen\u0026amp;#34;.freeze.object_id =\u0026amp;gt; true I\u0026amp;rsquo;ve never used this method in practice, but I hope to someday.\n","permalink":"/tils/object-id/","summary":"Today I learned about the method __id__, aliased object_id, in Ruby. It returns a unique integer identifier for any Ruby object.\nA few examples:\n\u0026gt; Object.new.object_id =\u0026gt; 7024702983434 \u0026gt; name …","title":"Object ID"},{"content":"I love autojump. I use it because I think that remembering my location in a directory structure relative to other directories, so I can navigate with cd, is mental energy I could be using for something else.\nToday I learned that autojump has a jo command, which jumps to a directory and opens it in your file explorer.\njo Desktop This might be a throwaway lesson if I wasn\u0026amp;rsquo;t regularly sidetracked by the MacOS file explorer. Finding a file using the file explorer is a routine operation I have yet to do fluidly on a Mac. This helps!\n","permalink":"/tils/autojump-and-open/","summary":"I love autojump. I use it because I think that remembering my location in a directory structure relative to other directories, so I can navigate with cd, is mental energy I could be using for …","title":"Autojump and Open"},{"content":"When I rebase, I force-push altered history using the flag --force-with-lease instead of the commonly-used --force.\ngit push --force-with-lease But why? This is a TIL for me because I\u0026amp;rsquo;ve used this command for years without reading the docs.\nTake it away, docs:\nThis option allows you to say that you expect the history you are updating is what you rebased and want to replace. If the remote ref still points at the commit you specified, you can be sure that no other people did anything to the ref.\nPractically, this says: \u0026amp;ldquo;I changed history and I\u0026amp;rsquo;m overwriting it forcefully. …","permalink":"/tils/force-with-lease/","summary":"When I rebase, I force-push altered history using the flag --force-with-lease instead of the commonly-used --force.\ngit push --force-with-lease But why? This is a TIL for me because I\u0026rsquo;ve used …","title":"Force with Lease"},{"content":"Here\u0026amp;rsquo;s some code that made me do a double take.\nsomething = items[nil] It\u0026amp;rsquo;s that [nil]. What\u0026amp;rsquo;s going on here?\nIt turns out that items is the return of a call to Ruby\u0026amp;rsquo;s group_by. group_by groups by whatever the block evaluates to, including nil.\n[ { shipping: \u0026amp;#39;fedex\u0026amp;#39;, id: 1 }, { shipping: \u0026amp;#39;fedex\u0026amp;#39;, id: 2 }, { id: 3 } ].group_by { |item| item[:shipping] } =\u0026amp;gt; { \u0026amp;#39;fedex\u0026amp;#39; =\u0026amp;gt; [{ shipping: \u0026amp;#39;fedex\u0026amp;#39;, id: 1 }, { shipping: \u0026amp;#39;fedex\u0026amp;#39;, id: 2 }], nil =\u0026amp;gt; [{ id: 3 }] } Hence the key nil.\nHere\u0026amp;rsquo;s another technique that I think is …","permalink":"/tils/group-by-with-default/","summary":"Here\u0026rsquo;s some code that made me do a double take.\nsomething = items[nil] It\u0026rsquo;s that [nil]. What\u0026rsquo;s going on here?\nIt turns out that items is the return of a call to Ruby\u0026rsquo;s …","title":"`group_by` with default"},{"content":"Jira has a hidden ticket \u0026amp;lsquo;Actions menu\u0026amp;rsquo; that is really helpful.\nWhen a Jira ticket is in focus, type ..\nThis opens a searchable menu with \u0026amp;lsquo;Issue actions\u0026amp;rsquo; (\u0026amp;lsquo;Attach\u0026amp;rsquo;, \u0026amp;lsquo;Link Issue\u0026amp;rsquo;, etc.) and \u0026amp;lsquo;Workflow\u0026amp;rsquo; state advancements (\u0026amp;lsquo;To Do\u0026amp;rsquo;, \u0026amp;lsquo;Close\u0026amp;rsquo;, etc.). Almost any action you\u0026amp;rsquo;d want to take on a ticket is there.\n","permalink":"/tils/jira-actions-menu/","summary":"Jira has a hidden ticket \u0026lsquo;Actions menu\u0026rsquo; that is really helpful.\nWhen a Jira ticket is in focus, type ..\nThis opens a searchable menu with \u0026lsquo;Issue actions\u0026rsquo; (\u0026lsquo;Attach\u0026rsquo;, …","title":"Jira Actions Menu"},{"content":"What should you focus on at the beginning of your programming career? My unflashy answer is: being really good at programming.\n\u0026amp;ldquo;Be So Good They Can\u0026amp;rsquo;t Ignore You\u0026amp;rdquo; My thoughts here are borrowed from Steve Martin, who advises entertainers to \u0026amp;ldquo;be so good they can\u0026amp;rsquo;t ignore you.\u0026amp;rdquo; When you\u0026amp;rsquo;re objectively great at your job, things start to get easier.\nProgramming is tough. There is an insane amount to learn across languages, platforms, and tools. You\u0026amp;rsquo;re going to be building mental models, pushing your limitations, and considering counter-intuitive ideas. …","permalink":"/posts/get-good/","summary":"What should you focus on at the beginning of your programming career? My unflashy answer is: being really good at programming.\n","title":"Why Getting Good Solves Most Career Problems"},{"content":"When developing a new feature in a brownfield app that hasn\u0026amp;rsquo;t been subject to consistent autoformatter treatment, turn off your autoformatter.\nAutoformatting is great. I advocate for it. But it can be distracting when trying to read through your new work. The hard-won changes you make get lost in the mix of automated changes. Once you\u0026amp;rsquo;ve got the feature working, turn on the autoformatter and get that automatic quality.\nThis is a pretty niche piece of advice, but it helped me today.\n","permalink":"/tils/turn-off-autoformatter-for-new-features/","summary":"When developing a new feature in a brownfield app that hasn\u0026rsquo;t been subject to consistent autoformatter treatment, turn off your autoformatter.\nAutoformatting is great. I advocate for it. But it …","title":"Turn off Autoformatter for New Features"},{"content":"Here\u0026amp;rsquo;s an observation from a month of developing for iOS. There can be a delay on any action taken in the App Store Connect console that you should plan for.\nActions like uploading a build, adding compliance, or adding testers might not be reflected in the App Store Connect console for minutes or hours.\nPatience. Instead of rebuilding or rage-clicking, just wait a bit for the processes to complete.\nAs signal, Apple often sends an email when some actions are completed, like releasing a build: \u0026amp;ldquo;Your App - Version 4.7.5 (216) for iOS is now available to test\u0026amp;rdquo;. If you\u0026amp;rsquo;re …","permalink":"/tils/app-store-delays/","summary":"Here\u0026rsquo;s an observation from a month of developing for iOS. There can be a delay on any action taken in the App Store Connect console that you should plan for.\n","title":"App Store Delays"},{"content":"When working on a React Native app on the iOS side, switching between branches with different dependencies can wreak havoc on your development environment. To ensure they\u0026amp;rsquo;re you\u0026amp;rsquo;re running the right libraries, blow everything away and start over:\nrm -rf node_modules \u0026amp;amp;\u0026amp;amp; yarn \u0026amp;amp;\u0026amp;amp; (cd ios \u0026amp;amp;\u0026amp;amp; pod deintegrate \u0026amp;amp;\u0026amp;amp; pod install) Replace yarn with npm install if you\u0026amp;rsquo;re using NPM.\n","permalink":"/tils/clear-ios-mobile-dependencies/","summary":"When working on a React Native app on the iOS side, switching between branches with different dependencies can wreak havoc on your development environment. To ensure they\u0026rsquo;re you\u0026rsquo;re running …","title":"Clear iOS Mobile Dependencies"},{"content":"The return of React Router\u0026amp;rsquo;s useLocation hook has a key called state which contains values passed during a redirect or navigation. But where is that state stored by the browser, and how do we clear it?\nI found it in Chrome on window.history.state:\n\u0026amp;gt; window.history.state \u0026amp;lt; { \u0026amp;#34;usr\u0026amp;#34;: { \u0026amp;#34;pageTitle\u0026amp;#34;: \u0026amp;#34;Redirected!\u0026amp;#34; }, \u0026amp;#34;key\u0026amp;#34;: \u0026amp;#34;31ytq582\u0026amp;#34;, \u0026amp;#34;idx\u0026amp;#34;: 4 } And cleared it using replaceState:\n\u0026amp;gt; window.history.replaceState({}, \u0026amp;#39;\u0026amp;#39;) \u0026amp;lt; undefined \u0026amp;gt; window.history.state \u0026amp;lt; {} Window.history docs\n","permalink":"/tils/clear-react-routers-location-state-from-the-console/","summary":"The return of React Router\u0026rsquo;s useLocation hook has a key called state which contains values passed during a redirect or navigation. But where is that state stored by the browser, and how do we …","title":"Clear React Router's Location State"},{"content":"The import statements at the top of a JavaScript component file can be a confusing, duplicative, churning mess. Is there a way to organize them that makes sense and scales? In this post, I\u0026amp;rsquo;d like to share the way I handle this detail.\nWho Cares? Wait; who cares? Isn\u0026amp;rsquo;t this like discussing the color of the bike shed?\nYears of programming with other humans has taught me that, for loosely-defined tasks like this, any convention is better than no convention. Without it, you\u0026amp;rsquo;ll see in many, many codebases:\nInexplicable whitespace (or lack of) Duplicated references to libraries …","permalink":"/posts/javascript-import-opinions/","summary":"The import statements at the top of a JavaScript component file can be a confusing, duplicative, churning mess. Is there a way to organize them that makes sense and scales? In this post, I\u0026rsquo;d …","title":"How to Organize JavaScript Imports"},{"content":"When I review code, I sometimes request that refactoring changes be removed. Even when I think the changes are objective improvements, and even when they support my personal preferences. My reasoning? Refactors are not free.\nIn this post, I\u0026amp;rsquo;ll list a few times when I think a refactor is worse than leaving the code unchanged.\nNo automated tests Do you have automated tests? Can you run them? Do you trust them? If you don\u0026amp;rsquo;t, don\u0026amp;rsquo;t refactor.\nA mentor once told me: \u0026amp;ldquo;refactoring without tests is not a thing.\u0026amp;rdquo; In other words, refactoring without tests is not refactoring, …","permalink":"/posts/when-should-i-not-refactor/","summary":"When I review code, I sometimes request that refactoring changes be removed. Even when I think the changes are objective improvements, and even when they support my personal preferences. My reasoning? …","title":"Write Better Code by Knowing When Not To Refactor"},{"content":"To get mobile scroll behavior on the XCode Simulator, inside the simulator window you must left click, then scroll.\n","permalink":"/tils/scroll-on-xcode-simulator/","summary":"To get mobile scroll behavior on the XCode Simulator, inside the simulator window you must left click, then scroll.\n","title":"Scroll on XCode Simulator"},{"content":"On a non-trivial Rails app, the output from rake routes will be huge. Many people develop a reflex for searching through this output via something like:\n$ rake routes | grep \u0026amp;#34;\u0026amp;lt;string\u0026amp;gt;\u0026amp;#34; Which filters the output to matches on the string. I\u0026amp;rsquo;d like to suggest a better option: use a command-line search tool that highlights the output! Here\u0026amp;rsquo;s that same command with RipGrep:\n$ rake routes | rg \u0026amp;#34;\u0026amp;lt;string\u0026amp;gt;\u0026amp;#34; The results are the same (although much faster 😈), but even better is that that each match will be colorized. It\u0026amp;rsquo;s bright red in my terminal. The …","permalink":"/tils/highlighted-routes-searching/","summary":"On a non-trivial Rails app, the output from rake routes will be huge. Many people develop a reflex for searching through this output via something like:\n$ rake routes | grep \u0026#34;\u0026lt;string\u0026gt;\u0026#34; …","title":"Highlighted Routes Searching"},{"content":"Here\u0026amp;rsquo;s a scenario: you\u0026amp;rsquo;re applying for your first tech job. You\u0026amp;rsquo;ve found a bunch of hands-on tutorials and you\u0026amp;rsquo;re speeding through them. When you finish, you upload the code to Github and add a new line to the \u0026amp;lsquo;Projects\u0026amp;rsquo; section of your resume. Maybe you even deploy the code as a production website. My advice: stop.\nTutorials are fun: you get to work through tough problems, like building an application with authentication, social posts, and tests, and learn to deploy to production. It feels like you\u0026amp;rsquo;re accomplishing something.\nI call this an …","permalink":"/posts/the-tutorial-antipattern/","summary":"Here\u0026rsquo;s a scenario: you\u0026rsquo;re applying for your first tech job. You\u0026rsquo;ve found a bunch of hands-on tutorials and you\u0026rsquo;re speeding through them. When you finish, you upload the code to …","title":"The Tutorial Anti-Pattern"},{"content":"The \u0026amp;lsquo;shake\u0026amp;rsquo; is an important React Native feature; it opens the In-App Developer Menu. This menu lets you reload, open the inspector, monitor performance, and more. To \u0026amp;lsquo;shake\u0026amp;rsquo; your simulated device, press ^ ⌘ Z. An alternative hotkey is ⌘ D.\n","permalink":"/tils/react-native-shake/","summary":"The \u0026lsquo;shake\u0026rsquo; is an important React Native feature; it opens the In-App Developer Menu. This menu lets you reload, open the inspector, monitor performance, and more. To \u0026lsquo;shake\u0026rsquo; …","title":"React Native Shake"},{"content":"When you push an iOS build to TestFlight, what\u0026amp;rsquo;s the experience for your testers? Do they need to opt-in to test the latest build?\nSpeaking anecdotally as person who has pushed a build and plans to test it today, when you add that build to your test group, it is automatically downloaded as the default for each of your testers, replacing the previous build. You can visit the TestFlight app on your phone to see the apps you can test. The latest build (build number, an integer, not the SemVer version) should be automatically downloaded to your device.\n","permalink":"/tils/testflight-downloads-latest-build/","summary":"When you push an iOS build to TestFlight, what\u0026rsquo;s the experience for your testers? Do they need to opt-in to test the latest build?\nSpeaking anecdotally as person who has pushed a build and plans …","title":"TestFlight Downloads Latest Build"},{"content":"Rails\u0026amp;rsquo; fields_for helper inserts a hidden ID field into your form. This is generally helpful, because you\u0026amp;rsquo;ll want that information when you\u0026amp;rsquo;re updating the record. But what if you don\u0026amp;rsquo;t?\nThis is the scenario I encountered today: a \u0026amp;lsquo;read-only\u0026amp;rsquo; form nested inside a fields_for helper, with no inputs. It almost worked: unfortunately, due to the default ID-inserting behavior of fields_for, there was a hidden ID input. It was getting submitted with the rest of the form and breaking things.\ninclude_id: false is the option we need.\n\u0026amp;lt;%= person_form.fields_for …","permalink":"/tils/fields-for-exclude-id/","summary":"Rails\u0026rsquo; fields_for helper inserts a hidden ID field into your form. This is generally helpful, because you\u0026rsquo;ll want that information when you\u0026rsquo;re updating the record. But what if you …","title":"fields_for exclude ID"},{"content":"I recently delivered a form that was hidden via CSS, using the BassCSS hide class, and encountered an issue: iOS customers\u0026amp;rsquo; browsers were and trying to autofill this hidden form with their saved passwords. What was going on here?\nhide it imparts these styles on its target in my Chrome:\n.hide { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } This worked visually, but it didn\u0026amp;rsquo;t hide the form from iOS. Browsing the BassCSS docs, I learned that this is an \u0026amp;lsquo;accessible\u0026amp;rsquo; hide, meant to visually hide an element from view …","permalink":"/tils/use-display-none/","summary":"I recently delivered a form that was hidden via CSS, using the BassCSS hide class, and encountered an issue: iOS customers\u0026rsquo; browsers were and trying to autofill this hidden form with their saved …","title":"Use Display None"},{"content":"Working at a quality software development consultancy like Hashrocket early in your programming career could be a game-changing move.\nFor background, I worked for six and half years at Hashrocket in Chicago. I started as an apprentice and left as a senior developer. I worked with brilliant people on interesting, challenging, and profitable products. I got to write Ruby, Elixir, Node, React, SQL, and even some PHP, spoke at RailsConf and ElixirDaze, and drove internal projects.\nI loved it. And I think consultancies can be a valuable stop for a junior developer on the rise. Let me explain why. …","permalink":"/posts/consultancy-cycles/","summary":"Working at a quality software development consultancy like Hashrocket early in your programming career could be a game-changing move.\n","title":"Consultancy Cycles"},{"content":"Here\u0026amp;rsquo;s a scenario: you\u0026amp;rsquo;re hacking on Ruby code in pry, and it\u0026amp;rsquo;s a mess. Blocks chained on blocks, variables named a and other_one, assignments to _. You can understand it, but nobody else can, and one extra } breaks everything.\nThe solution is the edit command. Typing edit in pry loads the current buffer into your editor of choice. Jump in there and turn those inline blocks into do/end, assign some variables, write a method: iterate on some quality code in the comfortable, predictable setting of your text editor.\n","permalink":"/tils/edit-in-pry/","summary":"Here\u0026rsquo;s a scenario: you\u0026rsquo;re hacking on Ruby code in pry, and it\u0026rsquo;s a mess. Blocks chained on blocks, variables named a and other_one, assignments to _. You can understand it, but nobody …","title":"Edit in Pry"},{"content":"When chaining commands in the terminal, you may need to do things that affect the shell\u0026amp;rsquo;s environment, like change directories. Here\u0026amp;rsquo;s an example:\n$ yarn install \u0026amp;amp;\u0026amp;amp; cd ios \u0026amp;amp;\u0026amp;amp; pod install \u0026amp;amp;\u0026amp;amp; cd .. \u0026amp;amp;\u0026amp;amp; yarn build These cd\u0026amp;rsquo;s can clutter up a script, and they\u0026amp;rsquo;re incidental. My solution is to use a subshell (here\u0026amp;rsquo;s the explanation from man bash):\n(list) list is executed in a subshell environment (see COMMAND EXECUTION ENVIRONMENT below). Variable assignments and builtin commands that affect the shell\u0026amp;rsquo;s environment do not remain in …","permalink":"/tils/command-line-subshell/","summary":"When chaining commands in the terminal, you may need to do things that affect the shell\u0026rsquo;s environment, like change directories. Here\u0026rsquo;s an example:\n$ yarn install \u0026amp;\u0026amp; cd ios …","title":"Command Line Subshell"},{"content":"Ruby\u0026amp;rsquo;s \u0026amp;lsquo;principle of least surprise\u0026amp;rsquo; strikes me again. From the docs:\nEquality— Two hashes are equal if they each contain the same number of keys and if each key-value pair is equal to (according to Object#==) the corresponding elements in the other hash.\nAn example:\nhash = { 7 =\u0026amp;gt; 35, \u0026amp;#34;c\u0026amp;#34; =\u0026amp;gt; 2, \u0026amp;#34;a\u0026amp;#34; =\u0026amp;gt; 1 } doppelganger = { \u0026amp;#34;a\u0026amp;#34; =\u0026amp;gt; 1, \u0026amp;#34;c\u0026amp;#34; =\u0026amp;gt; 2, 7 =\u0026amp;gt; 35 } hash == doppelganger #=\u0026amp;gt; true ","permalink":"/tils/hash-equality/","summary":"Ruby\u0026rsquo;s \u0026lsquo;principle of least surprise\u0026rsquo; strikes me again. From the docs:\nEquality— Two hashes are equal if they each contain the same number of keys and if each key-value pair is equal …","title":"Hash Equality"},{"content":"Today I was investigating a Ruby Proc created in a gem, and I wanted to know how it was defined. Other than reading source code, there doesn\u0026amp;rsquo;t seem to be a good way to do this! Prove me wrong.\nWe can get close by using Ruby\u0026amp;rsquo;s #source_location function on Proc:\nsome_proc.source_location =\u0026amp;gt; [\u0026amp;#34;/gems/2.4.0/bundler/gems/spree/core/app/models/spree/order.rb\u0026amp;#34;, 34] This command shows us the line where Proc is defined. Go there to continue source-diving.\n","permalink":"/tils/show-proc-source-location/","summary":"Today I was investigating a Ruby Proc created in a gem, and I wanted to know how it was defined. Other than reading source code, there doesn\u0026rsquo;t seem to be a good way to do this! Prove me wrong. …","title":"Show Proc Source Location"},{"content":"Yesterday I wrote about Chrome Flags; today I\u0026amp;rsquo;ll explain why.\nChrome is currently rolling out sweeping changes to how it handles cookies. I learned a lot about it this week. To summarize, cookies that don\u0026amp;rsquo;t specify a SameSite attribute will be treated as SameSite=Lax by Chrome starting with version 91. Ask for those cookies from a different URL, and Chrome won\u0026amp;rsquo;t share them. This means that cookies intended for third-party or cross-site sharing must specify SameSite=None and Secure. You can opt into this behavior today with the aforementioned flags.\nSo what? A lot of …","permalink":"/tils/chrome-samesite-updates/","summary":"Yesterday I wrote about Chrome Flags; today I\u0026rsquo;ll explain why.\nChrome is currently rolling out sweeping changes to how it handles cookies. I learned a lot about it this week. To summarize, …","title":"Chrome SameSite Updates"},{"content":"Chrome is making changes all time time; ignore them at your peril. Today I discovered Chrome\u0026amp;rsquo;s Flags menu, which lets you opt-in early to features that may soon be the default. Explore it at:\nchrome://flags/\n","permalink":"/tils/chrome-flags/","summary":"Chrome is making changes all time time; ignore them at your peril. Today I discovered Chrome\u0026rsquo;s Flags menu, which lets you opt-in early to features that may soon be the default. Explore it at: …","title":"Chrome Flags"},{"content":"The HTML date input tag allows you to request dates in a web form.\n\u0026amp;lt;input type=\u0026amp;#34;date\u0026amp;#34;\u0026amp;gt; This tag is more semantic for date input than text, and provides a side benefit: Google Chrome will automatically display a calendar selector on the page.\n","permalink":"/tils/date-input-tag/","summary":"The HTML date input tag allows you to request dates in a web form.\n\u0026lt;input type=\u0026#34;date\u0026#34;\u0026gt; This tag is more semantic for date input than text, and provides a side benefit: Google Chrome will …","title":"Date Input Tag"},{"content":"Vim\u0026amp;rsquo;s filetype auto-detection is great for effortless syntax highlighting, but what if a certain kind of file (i.e. Ruby) contains lots of another kind of code (i.e. SQL)? The Ruby code will be highlighted and readable, the SQL a large monochrome blob. Hard to read and reason about. We can do better!\nOverride the automatic assignment with:\n:set ft=sql This command with no assignment returns the current setting:\n:set ft filetype=lua We can easily revert to the auto-detected extension as needed.\nh/t Josh Branchaud\n","permalink":"/tils/override-vims-filetype/","summary":"Vim\u0026rsquo;s filetype auto-detection is great for effortless syntax highlighting, but what if a certain kind of file (i.e. Ruby) contains lots of another kind of code (i.e. SQL)? The Ruby code will be …","title":"Override Vim's Filetype"},{"content":"Cucumber step definitions are useful for explaining in detail the actions a user might take on your site. But after a while, they become repetitive. Certain sequences of steps, like a user signing in, happen frequently in a robust test suite. We should not have to repeat them all every time we build a feature.\nOne solution is calling several steps from within another step:\nGiven \u0026amp;#39;I am a signed in developer\u0026amp;#39; do steps %Q{ Given I am a developer with credentials And I see the homepage When I click sign in Then I see the signin page When I enter my credentials And I click the sign in …","permalink":"/tils/steps-within-steps/","summary":"Cucumber step definitions are useful for explaining in detail the actions a user might take on your site. But after a while, they become repetitive. Certain sequences of steps, like a user signing in, …","title":"Steps Within Steps"},{"content":"You can move the cursor without arrow keys. Here is the keyboard equivalent for each.\nUp (\u0026amp;lsquo;previous\u0026amp;rsquo;): CTRL + P Down (\u0026amp;rsquo;next\u0026amp;rsquo;): CTRL + N Left (\u0026amp;lsquo;back\u0026amp;rsquo;): CTRL + B Right (\u0026amp;lsquo;forward\u0026amp;rsquo;): CTRL + F Mapping caps lock to CTRL makes these combinations very accessible.\n","permalink":"/tils/directional-commands/","summary":"You can move the cursor without arrow keys. Here is the keyboard equivalent for each.\nUp (\u0026lsquo;previous\u0026rsquo;): CTRL + P Down (\u0026rsquo;next\u0026rsquo;): CTRL + N Left (\u0026lsquo;back\u0026rsquo;): CTRL + B …","title":"Directional Commands"},{"content":"After writing the other day about why you might not want to use simple getters, I decided that I wanted to eliminate all such methods from a project.\nHere is the regex I wrote to isolate the pattern:\nag \u0026amp;#39;def (\\w+);?\\s+@\\1;?\\s+end\u0026amp;#39; The semicolon catches one-line getter methods as well as the more common three-line.\n","permalink":"/tils/finding-getters/","summary":"After writing the other day about why you might not want to use simple getters, I decided that I wanted to eliminate all such methods from a project.\nHere is the regex I wrote to isolate the pattern: …","title":"Finding Getters"},{"content":"The command md5 \u0026amp;lt;file\u0026amp;gt; generates a unique 32-digit hexadecimal number. This can serve as a signature for a file in its particular state, letting you know when it has changed.\nExample usage:\n$ touch test.txt $ md5 test.txt MD5 (test.txt) = d41d8cd98f00b204e9800998ecf8427e $ echo \u0026amp;#39;some content\u0026amp;#39; \u0026amp;gt; test.txt $ md5 test.txt MD5 (test.txt) = eb9c2bf0eb63f3a7bc0ea37ef18aeba5 ","permalink":"/tils/md5-file-signatures/","summary":"The command md5 \u0026lt;file\u0026gt; generates a unique 32-digit hexadecimal number. This can serve as a signature for a file in its particular state, letting you know when it has changed.\nExample usage:\n$ …","title":"MD5 File Signatures"},{"content":"Recently while trying to fix a printer I used lpr a bunch of times. It\u0026amp;rsquo;s not exactly new to me, but never fails to surprise people when I use it.\nlpr submits files for printing to your default printer in OSX.\nPrint a file:\nlpr README.md Print the current file in your Vim session, with a cool job name:\n:! lpr -T \u0026amp;#39;cool job name\u0026amp;#39; % Print two copies to a specific printer:\nlpr -# 2 -P specific_printer README.md This is an invaluable command-line trick.\n","permalink":"/tils/printing-with-lpr/","summary":"Recently while trying to fix a printer I used lpr a bunch of times. It\u0026rsquo;s not exactly new to me, but never fails to surprise people when I use it.\nlpr submits files for printing to your default …","title":"Printing with lpr"},{"content":"Reverse a string with the rev command.\n$ echo \u0026amp;#39;test\u0026amp;#39; | rev tset It also works with files.\n$ rev Procfile br.amup/gifnoc C- amup cexe eldnub :bew ","permalink":"/tils/reverse-a-string/","summary":"Reverse a string with the rev command.\n$ echo \u0026#39;test\u0026#39; | rev tset It also works with files.\n$ rev Procfile br.amup/gifnoc C- amup cexe eldnub :bew ","title":"Reverse a String"},{"content":"Previously run commands can be viewed with the history command.\n$ history 10048 git checkout master 10049 gpr 10050 rake With this list, you can rerun any command using !n:\n$ !10048 Already on \u0026amp;#39;master\u0026amp;#39; The command !! prints the last command you ran, then runs it. Here is an example:\n$ ls LICENSE.md README.md bash cucumber rails $ !! ls LICENSE.md README.md bash cucumber rails Replace the second ! with the first few letters of a command you have previously run, and bash will search for, print, and run the most recent instance.\n$ !rsp rspec spec/models/user.rb ... ","permalink":"/tils/run-previous-command/","summary":"Previously run commands can be viewed with the history command.\n$ history 10048 git checkout master 10049 gpr 10050 rake With this list, you can rerun any command using !n:\n$ !10048 Already on …","title":"Run Previous Command"},{"content":"I like a quiet computer. But I also like tab-completing commands in the terminal, which produces a necessary alert from time to time.\nA solution I found today is to use OSX Accessibility features to add a screen flash on alerts, then disable the sounds.\nSystem Preferences \u0026amp;gt; Accessibility \u0026amp;gt; Hearing menu, choose \u0026amp;lsquo;Audio\u0026amp;rsquo; \u0026amp;gt; Check \u0026amp;lsquo;Flash the screen when an alert occurs\u0026amp;rsquo;\n","permalink":"/tils/screen-flashing-in-osx/","summary":"I like a quiet computer. But I also like tab-completing commands in the terminal, which produces a necessary alert from time to time.\nA solution I found today is to use OSX Accessibility features to …","title":"Screen Flashing in OSX"},{"content":"Processes on any POSIX-compliant computer can be sent to the background with CTRL-Z (\u0026amp;lt;prefix\u0026amp;gt; + Z for the tmux-ers) and returned to the foreground with fg.\nHere is an example:\nuser@computer:~% ping www.google.com PING www.google.com (74.125.228.212): 56 data bytes 64 bytes from 74.125.228.212: icmp_seq=0 ttl=52 time=41.574 ms 64 bytes from 74.125.228.212: icmp_seq=1 ttl=52 time=42.836 ms 64 bytes from 74.125.228.212: icmp_seq=2 ttl=52 time=53.527 ms ^Z zsh: suspended ping www.google.com user@computer:~% fg [1] + continued ping www.google.com 64 bytes from 74.125.228.212: icmp_seq=3 …","permalink":"/tils/send-processes-to-the-background/","summary":"Processes on any POSIX-compliant computer can be sent to the background with CTRL-Z (\u0026lt;prefix\u0026gt; + Z for the tmux-ers) and returned to the foreground with fg.\nHere is an example:\nuser@computer:~% …","title":"Send Processes to the Background"},{"content":"Today I was trying to restore a production Postgres dump on a remote machine, to test some migrations. I haven\u0026amp;rsquo;t granted myself Heroku production database access, hence the SSH into a machine with a Heroku user that has access.\nI quickly hit a problem— this remote machine runs Postgres using Postgres.app. Without that program, I\u0026amp;rsquo;m out of luck. No local Postgres server, no production database restore, no testing.\nIt turns out you can start Postgres.app from the command line. Find the executable and call it.\njake@remote-machine:$ /Applications/Postgres.app/Contents/MacOS/Postgres …","permalink":"/tils/start-postgresapp-from-the-command-line/","summary":"Today I was trying to restore a production Postgres dump on a remote machine, to test some migrations. I haven\u0026rsquo;t granted myself Heroku production database access, hence the SSH into a machine …","title":"Start Postgres.app from the Command Line"},{"content":"Have you ever been working in the terminal and found yourself repeating the same command many times? Delegate that work to the computer.\nwatch comes with Linux and can be installed on OSX via homebrew. It executes a program periodically, defaulting to every two seconds.\nWe used it today while writing a database backup script. Instead of checking our dump directory every time a cron job executed, we ran watch ls, and watched the script succeed or fail with live updates.\nman watch for more information.\n","permalink":"/tils/watch-that-program/","summary":"Have you ever been working in the terminal and found yourself repeating the same command many times? Delegate that work to the computer.\nwatch comes with Linux and can be installed on OSX via …","title":"Watch That Program"},{"content":"Today I got to see the :ExtractRspecLet command from the vim-weefactor plugin. It does what the names suggests, converting this:\n# spec/model/foobar_spec.rb foo = FactoryGirl.create :foobar To this:\n# spec/model/foobar_spec.rb let(:foo) { FactoryGirl.create :foobar } It also moved the new let from inside my it block to right underneath my context block. Awesome!\nh/t Josh Davey and Dillon Hafer\n","permalink":"/tils/extractrspeclet/","summary":"Today I got to see the :ExtractRspecLet command from the vim-weefactor plugin. It does what the names suggests, converting this:\n# spec/model/foobar_spec.rb foo = FactoryGirl.create :foobar To this:\n# …","title":"ExtractRSpecLet"},{"content":"To update rubygems:\ngem install rubygems-update update_rubygems gem update --system ","permalink":"/tils/upgrade-rubygems/","summary":"To update rubygems:\ngem install rubygems-update update_rubygems gem update --system ","title":"Upgrade Rubygems"},{"content":"If you\u0026amp;rsquo;ve been mucking around in your Ruby dependencies, bundle pristine is your friend; it resets all of the gems in your Gemfile to their pristine condition.\nThis can be a slow on a big project. Target the specific gems you worked on today with:\n$ bundle pristine \u0026amp;lt;gemname\u0026amp;gt; ","permalink":"/tils/reset-a-gem/","summary":"If you\u0026rsquo;ve been mucking around in your Ruby dependencies, bundle pristine is your friend; it resets all of the gems in your Gemfile to their pristine condition.\nThis can be a slow on a big …","title":"Reset a Gem"},{"content":"Today I\u0026amp;rsquo;d like to explain the sub, or subject, registered claim of a JSON Web Token.\nRegistered claims are predefined fields of the JWT that are not mandatory but recommended. Like all JWT claims, they must be unique.\nsub is the subject of the JWT– the user who requested the token, typically an email address. By using it, we can send and receive a small, unique piece of data about a user along with each request, without any additional requests.\nThis means that after a user authenticates and we grant a token, we can use that same token to figure out who the user is, plus show them a …","permalink":"/posts/til-what-is-the-jwt-sub/","summary":"Today I\u0026rsquo;d like to explain the sub, or subject, registered claim of a JSON Web Token.\n","title":"What is the Sub Claim of a JSON Web Token?"},{"content":"Let\u0026amp;rsquo;s look at the proxy pattern in Ruby.\nHere\u0026amp;rsquo;s a Ruby class that claps:\nclass Hands def clap \u0026amp;#34;👏\u0026amp;#34; end end And here\u0026amp;rsquo;s me using an instance of it to do something surprising.\n\u0026amp;gt;\u0026amp;gt; 3.times { hands.clap } \u0026amp;#34;👏\u0026amp;#34; \u0026amp;#34;👏\u0026amp;#34; \u0026amp;#34;👏\u0026amp;#34; =\u0026amp;gt; 3 \u0026amp;gt;\u0026amp;gt; hands.times_called(:clap) =\u0026amp;gt; 3 times_called is not defined in Hands, or any parent of it. So what gives? Did I leave it off the first code block? What\u0026amp;rsquo;s going on here?\nI recently learned about and had the opportunity to implement a proxy class. In this post, I\u0026amp;rsquo;ll share what I learned, showing how …","permalink":"/posts/proxy-pattern/","summary":"Let\u0026rsquo;s look at the proxy pattern in Ruby.\n","title":"Augmenting an Object With the Proxy Pattern"},{"content":"A concept that I admire is \u0026amp;lsquo;Doing the Hard Thing First\u0026amp;rsquo; via Ben Orenstein. Ben described on the Art of Product Podcast Episode 50. Here\u0026amp;rsquo;s my summary:\nChoose the hard part of the problem, the part you’re unsure about, the part that success depends on, and solve that first!\nThe Hard Part of the Problem The hard part of the problem will be the most differentiating, and therefore valuable. If it was easy, there would be no opportunity. You\u0026amp;rsquo;re lucky that Schlep Blindness has failed to hide it from you. It might be impossible, at least the way you\u0026amp;rsquo;re thinking about it. …","permalink":"/posts/do-the-hard-things-first/","summary":"A concept that I admire is \u0026lsquo;Doing the Hard Thing First\u0026rsquo; via Ben Orenstein. Ben described on the Art of Product Podcast Episode 50. Here\u0026rsquo;s my summary:\nChoose the hard part of the …","title":"Do the Hard Things First"},{"content":"I\u0026amp;rsquo;ve been learning a bit of Python this Fall to facilitate conversations with a mentee. In this post, I\u0026amp;rsquo;m going to share the first Python function I\u0026amp;rsquo;ve ever written, a solution to the Exercism challenge \u0026amp;lsquo;Raindrops\u0026amp;rsquo;.\nIs this good Python code? I don\u0026amp;rsquo;t know; that\u0026amp;rsquo;s why I\u0026amp;rsquo;m posting it here. I\u0026amp;rsquo;ve read $ python3 -c \u0026#39;import this\u0026#39; yet am still gloriously ignorant. Below I\u0026amp;rsquo;ll share the problem, my solution, and a code self-review via praise and criticism.\nProblem Description Your task is to convert a number into a string that contains raindrop …","permalink":"/posts/exercism-raindrops-in-python/","summary":"I\u0026rsquo;ve been learning a bit of Python this Fall to facilitate conversations with a mentee. In this post, I\u0026rsquo;m going to share the first Python function I\u0026rsquo;ve ever written, a solution to …","title":"Exercism Raindrops in Python"},{"content":"There is a glut of programming information online. You can\u0026amp;rsquo;t read it all, and you shouldn\u0026amp;rsquo;t, because much of it is outdated, wrong, or irrelevant to you.\nSo, what is the signal in this noise? I recommend aggregators, blogs, pair programming. And I offer a gentle recommendation against tutorials.\nAggregators I\u0026amp;rsquo;ve summarized this idea before in a Hashrocket blog post called \u0026amp;lsquo;How to Prepare for a Technical Interview\u0026amp;rsquo;:\nI advocate a passive approach: whatever watercooler you frequent (Twitter, Medium, RSS, Reddit, email mailing lists, etc.), find the aggregators. These …","permalink":"/posts/programming-resources-for-beginners/","summary":"There is a glut of programming information online. You can\u0026rsquo;t read it all, and you shouldn\u0026rsquo;t, because much of it is outdated, wrong, or irrelevant to you.\nSo, what is the signal in this …","title":"Programming Resources for Beginners"},{"content":"I love Vim. Folks who\u0026amp;rsquo;ve programmed with me, or attended a Vim Meetup when I was an organizer, can attest. When I was learning to code, getting fast at Vim changed everything for me. After almost a decade using this editor, here are my personal configurations.\nMy Vim Configuration My .vimrc is broken up into groups. If you open this file in Vim, you\u0026amp;rsquo;ll see why: it\u0026amp;rsquo;s long, and so it\u0026amp;rsquo;s folded by groups as a default. This is one of many tips I picked up reading Learn VimScript The Hard Way by Steve Losh, and the dotfiles of programmers I admire.\nI\u0026amp;rsquo;ve commented the …","permalink":"/posts/my-annotated-vimrc/","summary":"I love Vim. Folks who\u0026rsquo;ve programmed with me, or attended a Vim Meetup when I was an organizer, can attest. When I was learning to code, getting fast at Vim changed everything for me. After …","title":"My Annotated Vim Configuration File"},{"content":"Programming is a new and abstract field, and so we place great emphasis on ideas. When I find one that sticks with me, I end up sharing it again and again. These are some of my favorites.\nJeff Atwood, The First Rule of Programming: It\u0026amp;rsquo;s Always Your Fault. I like to answer questions on Stack Overflow, and a decent number of questions on that website have a title like \u0026amp;ldquo;I Found a Bug in React\u0026amp;rdquo;, and a question that starts with: \u0026amp;ldquo;I\u0026amp;rsquo;m brand new to React.\u0026amp;rdquo; It\u0026amp;rsquo;s human to assume there\u0026amp;rsquo;s a problem with your tools. Especially starting out, it\u0026amp;rsquo;s more …","permalink":"/posts/essays-on-programming-i-think-about-a-lot/","summary":"Programming is a new and abstract field, and so we place great emphasis on ideas. When I find one that sticks with me, I end up sharing it again and again. These are some of my favorites.\n","title":"Essays on Programming I Think About a Lot"},{"content":"I\u0026amp;rsquo;ve been working on a development roadmap for my projects, and wanted to share my process. Consider this my recipe to turn an idea into software.\nThe purpose of codifying a workflow like this is to make working with me a predictable and thorough experience. Writing software is pretty hard; it\u0026amp;rsquo;s much harder when you don\u0026amp;rsquo;t have a plan.\nEvery item here is à la carte– I wouldn\u0026amp;rsquo;t typically do more, but I might do less on projects of modest scope.\nWhat do I have to show for these words? Several dozen production greenfield and legacy client projects, an in-development startup …","permalink":"/posts/my-development-roadmap/","summary":"I\u0026rsquo;ve been working on a development roadmap for my projects, and wanted to share my process. Consider this my recipe to turn an idea into software.\n","title":"My Development Roadmap"},{"content":"Here\u0026amp;rsquo;s a familiar scenario: you open a new terminal, and before the prompt appears, you see the following.\nYou have new mail.\nWhat\u0026amp;rsquo;s going on here?\nExplanation Events in your operating system can send messages to your mail queue. Although it\u0026amp;rsquo;s tempting to ignore them because they aren\u0026amp;rsquo;t colored red or filled with hyperbolic language, you should not ignore these messages. In many cases, your operating system is trying to tell you something important.\nInspect these messages with the mail command.\n$ mail \u0026amp;#34;/var/mail/jake\u0026amp;#34;: 60 messages 60 unread ... Action Uh oh, 60 …","permalink":"/posts/you-have-new-mail/","summary":"Here\u0026rsquo;s a familiar scenario: you open a new terminal, and before the prompt appears, you see the following.\nYou have new mail.\nWhat\u0026rsquo;s going on here?\n","title":"You Have New Mail"},{"content":"I\u0026amp;rsquo;m working on a team where we keep a clone of the development branch (the main place where work is done), used to deploy to a QA environment. The benefits of this branching technique are:\nclone is isolated from work It\u0026amp;rsquo;s easy to tell what was deployed to QA– clone is the source of truth Instead of merging, we just reset clone to match the development branch.\nHere\u0026amp;rsquo;s the technique:\n# Update development, however you prefer. # I like to rebase. 🤘 $ git checkout development $ git pull --rebase # Reset clone to match development $ git checkout clone $ git reset --hard development …","permalink":"/posts/duplicate-your-developent-branch-for-qa/","summary":"I\u0026rsquo;m working on a team where we keep a clone of the development branch (the main place where work is done), used to deploy to a QA environment. The benefits of this branching technique are:\nclone …","title":"Duplicate your Development Branch for QA"},{"content":"Unused dependencies are bad: they increase the size of your project, slow down your processes, require upgrades, and send incorrect messages to fellow developers about what\u0026amp;rsquo;s important. Make your project better by periodically auditing your dependencies, and removing those that are unused.\nIf a dependency is in your bundle and is not being used right now, get rid of it! Legacy codebases are littered with rainy-day libraries. Rip them out and start again.\nCleanup JavaScript dependencies For a JavaScript project, there are two techniques I\u0026amp;rsquo;ve used: manual, and what I\u0026amp;rsquo;ll call …","permalink":"/posts/how-i-clean-up-unused-javascript-dependencies/","summary":"Unused dependencies are bad: they increase the size of your project, slow down your processes, require upgrades, and send incorrect messages to fellow developers about what\u0026rsquo;s important. Make …","title":"How I Clean Up JavaScript Dependencies"},{"content":"Here\u0026amp;rsquo;s my annual professional review covering 2018.\nMilestones Here are my milestones from 2019. This year, I:\n🎉 Ported this blog from Rails to Gatsby.js. I am ecstatic about this change. Writing React and markdown, and deploying with GitHub, makes for an effortless workflow. I also open sourced the codebase. I\u0026amp;rsquo;d love some PR\u0026amp;rsquo;s or issues on the repo. Received a challenge coin from Code Platoon for work as a volunteer. Thank you Rod! I love being a part of this community. Created and administered, along with my colleague Suzanne, an internal Developer\u0026amp;rsquo;s Survey for …","permalink":"/posts/my-annual-review-2019/","summary":"Here\u0026rsquo;s my annual professional review covering 2018.\n","title":"My Annual Review 2019"},{"content":"Here\u0026amp;rsquo;s my list of favorite programming books.\nIt\u0026amp;rsquo;s been said that most programmers read less than one professional book per year. I get it. Books about programming can be have short shelf lives, as well as be expensive, hard to find, and challenging to read.\nBut in my experience, great programmers have read a lot of them anyway. There are concepts defined in classic programming books that are ubiquitous in the field. When you read those books yourself, you absorb those concepts. Or at least develop informed critiques of them.\nSome of these books in this list are programming …","permalink":"/posts/developer-reading-list/","summary":"Here\u0026rsquo;s my list of favorite programming books.\n","title":"Technologist Reading List"},{"content":"I\u0026amp;rsquo;ve practiced TDD (Test-Driven-Development) for years and feel knowledgeable about when it\u0026amp;rsquo;s useful and when it isn\u0026amp;rsquo;t. In this post, I\u0026amp;rsquo;d like to summarize what I\u0026amp;rsquo;ve learned.\nMy definition of TDD It\u0026amp;rsquo;s worth mentioning first that there are different versions of TDD. There\u0026amp;rsquo;s outside-in or black-box TDD, red-green-refactor, BDD, ATDD, and more. They\u0026amp;rsquo;re all in the same family, but they are distinct. Getting to caught up on terminology can quickly distract from the core concepts they share.\nTDD is defined by actions. Here\u0026amp;rsquo;s my take on classic …","permalink":"/posts/intro-to-tdd/","summary":"I\u0026rsquo;ve practiced TDD (Test-Driven-Development) for years and feel knowledgeable about when it\u0026rsquo;s useful and when it isn\u0026rsquo;t. In this post, I\u0026rsquo;d like to summarize what I\u0026rsquo;ve …","title":"A Short Introduction to Test-Driven-Development (TDD)"},{"content":"I\u0026amp;rsquo;ve been doing Exercism\u0026amp;rsquo;s TypeScript exercises, and wanted to share my pangram-checking utility.\nA pangram is a sentence that contains each of the 26 English letters at least once. Here\u0026amp;rsquo;s my TypeScript class to determine if a sentence is a pangram.\nclass Pangram { input: string; constructor(input: string) { this.input = input.toLowerCase(); } isPangram(): boolean { const letters = this.input.replace(/[^a-z]/g, \u0026amp;#39;\u0026amp;#39;); return new Set(letters).size === 26; } } export default Pangram; isPangram/0 does the work: replacing all characters not a-z with empty strings (the …","permalink":"/posts/pangram-in-typescript/","summary":"I\u0026rsquo;ve been doing Exercism\u0026rsquo;s TypeScript exercises, and wanted to share my pangram-checking utility.\n","title":"Pangram in TypeScript"},{"content":"\u0026amp;ldquo;Select isn\u0026amp;rsquo;t broken.\u0026amp;rdquo; If you\u0026amp;rsquo;re working on a CSS file, and none of your changes are being applied, check for typos, crashed servers, misplaced files, etc. Once you\u0026amp;rsquo;ve ruled out simple mistakes, you might have a syntax error like this:\n.klass { opacity: 0.5; }; The trailing semicolon is incorrect, and none of the CSS below it can be understood by the browser.\nChange this to:\n.klass { opacity: 0.5; } There may be some environments where a broken CSS file fails loudly; Code Sandbox or Codepens are not among them. If you\u0026amp;rsquo;re changing CSS and nothing is …","permalink":"/tils/semicolon-breaks-everything/","summary":"\u0026ldquo;Select isn\u0026rsquo;t broken.\u0026rdquo; If you\u0026rsquo;re working on a CSS file, and none of your changes are being applied, check for typos, crashed servers, misplaced files, etc. Once you\u0026rsquo;ve …","title":"Semicolon Breaks Everything"},{"content":"I have an .rgignore file that ignores half of a monorepo that I almost never want to search in. Sometimes though, I do need to search that repo, and I\u0026amp;rsquo;d rather not edit my ignore file.\nHere\u0026amp;rsquo;s how I override it for one search:\nrg \u0026amp;#34;I mean,\u0026amp;#34; --no-ignore This will search in every directory, even those I\u0026amp;rsquo;m ignoring via the .rgignore.\n","permalink":"/tils/ignore-ripgrep-ignore/","summary":"I have an .rgignore file that ignores half of a monorepo that I almost never want to search in. Sometimes though, I do need to search that repo, and I\u0026rsquo;d rather not edit my ignore file. …","title":"Ignore Ripgrep Ignore"},{"content":"Adding | pbcopy to the end of any command will send the standard output to your clipboard.\nls -al | pbcopy inside a rails project directory allowed me to paste this:\nGemfile Gemfile.lock README.md Rakefile app bin config config.ru db features lib log public script spec test tmp vendor ","permalink":"/tils/standard-output-to-clipboard-buffer/","summary":"Adding | pbcopy to the end of any command will send the standard output to your clipboard.\nls -al | pbcopy inside a rails project directory allowed me to paste this:\nGemfile Gemfile.lock README.md …","title":"Pipe to pbcopy"},{"content":"A few weeks ago, I built an app with React.js and create-react-app that I call \u0026amp;lsquo;JavaScript Equality\u0026amp;rsquo;. View deployment here. This application demonstrates the JavaScript value-comparison operators == and ===. It\u0026amp;rsquo;s inspired by the JavaScript Equality Table.\nThe image above shows why the threequals is superior to the twoquals equals. If you want true equality, the kind of logic you\u0026amp;rsquo;d expect with any other programming language, use threequals.\nMy favorite implementation detail of this app is the data model, which is created in the constructor function:\nconst comparatorArray …","permalink":"/posts/javascript-equality/","summary":"A few weeks ago, I built an app with React.js and create-react-app that I call \u0026lsquo;JavaScript Equality\u0026rsquo;. View deployment here. This application demonstrates the JavaScript value-comparison …","title":"JavaScript Equality"},{"content":"My current favorite command line alias is mgrim, composed of four other aliases. Here\u0026amp;rsquo;s what it is, and what it does.\nalias gmr=\u0026amp;#39;gco master \u0026amp;amp;\u0026amp;amp; gpr \u0026amp;amp;\u0026amp;amp; gco - \u0026amp;amp;\u0026amp;amp; git rebase master\u0026amp;#39; alias grim=\u0026amp;#39;git rebase -i master\u0026amp;#39; alias mgrim=\u0026amp;#39;gmr \u0026amp;amp;\u0026amp;amp; grim\u0026amp;#39; Starting from a feature branch:\ngmr - checkout the master branch (gco is a Hashrocket alias for git checkout. Pull from origin/master and try to put my commits in the right place in history (gpr is a Hashrocket alias for git pull --rebase). Checkout my feature branch. Fast-forward my branch to the …","permalink":"/posts/mgrim-everything-updated-all-the-time/","summary":"My current favorite command line alias is mgrim, composed of four other aliases. Here\u0026rsquo;s what it is, and what it does.\n","title":"A Git Workflow to Keep Branches Current and PRs Clean"},{"content":"A user opened an issue in Tilex last week that I spent a significant amount of time thinking about and talking about (thanks JB) before responding to. Here\u0026amp;rsquo;s the truncated request:\nMay I ask you to compile a step-by-step list to set up a running TIL instance?\nTilex Issue #286\nI knew the answer was \u0026amp;rsquo;no\u0026amp;rsquo;, but I reflected on my answer for a long time. When does documentation become too specific? What\u0026amp;rsquo;s a fair request of an open source project maintainer\u0026amp;rsquo;s time? How do I decline a request while still thanking the requester and making sure the interaction remains …","permalink":"/posts/comprehensive-instructions/","summary":"A user opened an issue in Tilex last week that I spent a significant amount of time thinking about and talking about (thanks JB) before responding to. Here\u0026rsquo;s the truncated request:\nMay I ask you …","title":"Comprehensive Instructions"},{"content":"Recently I tried to answer a question that I think is common to consider when you\u0026amp;rsquo;re first learning about computer programming. I\u0026amp;rsquo;ll paraphrase:\nWhy can\u0026amp;rsquo;t there be an “universal” programming language that serves all purposes?\nThe number of programming languages in the world is dizzying. Names like JavaScript, Ruby, and C are known by programmers and non-programmers alike. Since technology constantly evolves, and learning many things takes more time than learning one thing, wouldn\u0026amp;rsquo;t a reasonable expectation be that we\u0026amp;rsquo;ll all someday coalesce around an ideal …","permalink":"/posts/the-universal-programming-language/","summary":"Recently I tried to answer a question that I think is common to consider when you\u0026rsquo;re first learning about computer programming. I\u0026rsquo;ll paraphrase:\nWhy can\u0026rsquo;t there be an “universal” …","title":"The Universal Programming Language"},{"content":"I\u0026amp;rsquo;ve been giving technical talks for a few years, and I\u0026amp;rsquo;m speaking at the Vim Chicago Meetup next month about integrating React with Vim. In this post, I\u0026amp;rsquo;m going to use that opportunity as an excuse to document my speaking process.\nSome smart people have written on this subject, and anything I\u0026amp;rsquo;d have to say is partly inspired by these ideas:\nConference Prompts: Or How to Submit Proposals and Influence People How to Prepare a Talk Improve Your Presentations In Under $50 Speaking.io What I learned from reading 429 conference proposals What Your Conference Proposal Is …","permalink":"/posts/how-i-talk/","summary":"I\u0026rsquo;ve been giving technical talks for a few years, and I\u0026rsquo;m speaking at the Vim Chicago Meetup next month about integrating React with Vim. In this post, I\u0026rsquo;m going to use that …","title":"How I Talk: My Guide to Tech Public Speaking"},{"content":"As soon as you can, just do the thing you have to do, and respond with: \u0026amp;ldquo;It\u0026amp;rsquo;s done.\u0026amp;rdquo;\nWhen a stakeholder asks for a change to the software I\u0026amp;rsquo;m building, my typical responses are, in order: \u0026amp;ldquo;Got it.\u0026amp;rdquo; Then: \u0026amp;ldquo;I\u0026amp;rsquo;m working on that issue.\u0026amp;rdquo; Finally: \u0026amp;ldquo;It\u0026amp;rsquo;s done.\u0026amp;rdquo; Here\u0026amp;rsquo;s an alternative: skip the first two conversations and go straight to the third.\nShip, then signal.\nUnderpromise; Overdeliver It\u0026amp;rsquo;s a variation on the idea of \u0026amp;ldquo;Underpromise; overdeliver.\u0026amp;rdquo; In this case, by waiting to respond, I\u0026amp;rsquo;m promising …","permalink":"/posts/its-done/","summary":"As soon as you can, just do the thing you have to do, and respond with: \u0026ldquo;It\u0026rsquo;s done.\u0026rdquo;\n","title":"Execution Credibility: Ship, Then Signal"},{"content":"There\u0026amp;rsquo;s a detail about Today I Learned some might find unusual: we never added a way to delete posts from the site. Why ignore a basic CRUD feature? We didn\u0026amp;rsquo;t ignore it. It was intentionally omitted.\nI\u0026amp;rsquo;ve thought about this decision a lot over the last two years. It\u0026amp;rsquo;s lasted over many pull requests, issues, forks, and discussions. I addressed it directly in a Github issue not long ago.\nPreventing deletes was a hack to make Today I Learned feel permanent. If the goal of the site was to show the world what we at Hashrocket are learning, why allow people to self-censor …","permalink":"/posts/dont-build-every-feature/","summary":"There\u0026rsquo;s a detail about Today I Learned some might find unusual: we never added a way to delete posts from the site. Why ignore a basic CRUD feature? We didn\u0026rsquo;t ignore it. It was …","title":"Don't Build Every Feature"},{"content":" Write programs that do one thing and do it well. –The Unix Philosophy\nI believe in working small.\nI love small Git commits and pull requests. When I commit, I like a short message. The code should only do what\u0026amp;rsquo;s described by that message, in limited migrations, each of which can be rolled back. The changes should touch a limited number of files, and leave a green test suite. Everything should be reversible without side effects.\nAnyone who has paired-programmed with me knows that I\u0026amp;rsquo;m quick to say \u0026amp;ldquo;Can we commit this now?\u0026amp;rdquo; I want to commit as soon as the tests are green, …","permalink":"/posts/work-small/","summary":" Write programs that do one thing and do it well. –The Unix Philosophy\nI believe in working small.\n","title":"Work Small"},{"content":"Tilex, our Phoenix port of Today I Learned, is coming really well. After a personal hiatus for RailsConf prep, I\u0026amp;rsquo;m back full-swing. My coworkers have really been bringing quality commits as well, and I feel we are very close to a successful port.\nThis experiment, for me, began with a desire for knowledge. What does a CMS look like in Elixir and Phoenix? Would it be harder to write and maintain? What would be the tradeoffs? Would Elixir\u0026amp;rsquo;s legendary fault-tolerance and concurrency matter for a glorified CRUD application?\nSince the initial commit, the mission has evolved. We are close …","permalink":"/posts/tilex-progress/","summary":"Tilex, our Phoenix port of Today I Learned, is coming really well. After a personal hiatus for RailsConf prep, I\u0026rsquo;m back full-swing. My coworkers have really been bringing quality commits as …","title":"What We Learned Porting a Rails App to Phoenix"},{"content":"I created this blog to reflect on my code and development as a programmer. In that spirit, I\u0026amp;rsquo;d like to make a pitch to anybody reading: you should blog.\nWhy? For me, it\u0026amp;rsquo;s been an invaluable opportunity to practice explaining my thoughts, and to watch myself grow.\nExplaining Thoughts Translating technical ideas into words is one of the great challenges of being a programmer. We have to master it. Communicating well with everyone on your team is crucial.\nThis blog forces me to reflect on my work. The process prompts the following questions:\nWhy did I write this code? Why did I choose …","permalink":"/posts/you-should-blog/","summary":"I created this blog to reflect on my code and development as a programmer. In that spirit, I\u0026rsquo;d like to make a pitch to anybody reading: you should blog.\n","title":"You Should Blog"},{"content":"We introduced some breaking database changes to our Phoenix port of Today I Learned on Friday; today I deployed them to staging. Resetting an Ecto Heroku database, with new seed data, turned out to be a little tricky, and I wanted to document what I learned.\nThis blog post assumes you already have the Heroku Toolbelt and an Ecto-supported application in production.\nFirst, make sure you deploy the breaking changes:\n$ git push \u0026amp;lt;your_remote\u0026amp;gt; master To drop a Heroku database, first we need the name of the database. DATABASE_URL is specific enough if there\u0026amp;rsquo;s only one, but I think …","permalink":"/posts/reset-an-ecto-heroku-database/","summary":"We introduced some breaking database changes to our Phoenix port of Today I Learned on Friday; today I deployed them to staging. Resetting an Ecto Heroku database, with new seed data, turned out to be …","title":"Reset an Ecto Heroku Postgres Database"},{"content":"When you command-click on a Chrome bookmarks folder, it opens every link in the folder in a new tab.\nI did this by accident; so what\u0026amp;rsquo;s the use case? I use bookmark folders to categorize my \u0026amp;lsquo;work links\u0026amp;rsquo;: hosting, error monitoring, Git hosting, online docs, etc. With this TIL, I could use this folder to open a new Chrome session and instantly load it with all the pages I need to be effective.\n","permalink":"/tils/open-every-site-in-a-chrome-bookmarks-folder-with-one-click/","summary":"When you command-click on a Chrome bookmarks folder, it opens every link in the folder in a new tab.\nI did this by accident; so what\u0026rsquo;s the use case? I use bookmark folders to categorize my …","title":"Open Every Site in a Chrome Bookmarks Folder"},{"content":"My first PR to a new Vim plugin was merged this week, check it out, adding non-recursive Vim mappings to vim-termbux.\nvim-termbux is a plugin by my friend Dillon Hafer that emulates the behavior of vim-turbux— sending test run output to somewhere outside of Vim– in iTerm. If you write a lot of tests in iTerm and Vim, but prefer to stay outside of Tmux for whatever reason (fingers don\u0026amp;rsquo;t like the mappings, etc.), than this plugin is for you.\nMy patch changes this:\nnmap \u0026amp;lt;leader\u0026amp;gt;t :call \u0026amp;lt;SID\u0026amp;gt;ITermTestRunnerLine()\u0026amp;lt;cr\u0026amp;gt; nmap \u0026amp;lt;leader\u0026amp;gt;T :call …","permalink":"/posts/vim-nonrecursive-mappings/","summary":"My first PR to a new Vim plugin was merged this week, check it out, adding non-recursive Vim mappings to vim-termbux.\n","title":"Vim Nonrecursive Mappings"},{"content":"Last week, I started a new project: porting Today I Learned from Ruby on Rails to Phoenix (Elixir).\nThe first few commits were pair programmed with Josh Branchaud, and I hope to work with him and other Rocketeers going forward.\nThe repo for the new codebase is here.\nThere\u0026amp;rsquo;s a lot of work to do to reproduce even the MVP functionality of TIL. We\u0026amp;rsquo;re using the \u0026amp;lsquo;Projects\u0026amp;rsquo; feature of Github to track progress right in the repo.\nPurpose The purpose of this project is to learn Phoenix, explore the Elixir community, and further develop myself and my colleagues as functional …","permalink":"/posts/porting-til-to-phoenix-initial-commits/","summary":"Last week, I started a new project: porting Today I Learned from Ruby on Rails to Phoenix (Elixir).\n","title":"Porting TIL from Rails to Phoenix: Initial Commits"},{"content":"Today while writing a Vim regex to change every instance of it (ignoring larger matches like itemized), we stumbled upon Vim regex word boundary matching.\nGiven this file:\nfoo foobar The following Vim regex will match on both the first and second line:\n:%s/foo/baz/g But with word boundaries, we\u0026amp;rsquo;ll only match (and change) the first line, because only the first foo is a standalone word:\n:%s/\\\u0026amp;lt;foo\\\u0026amp;gt;/baz/g ","permalink":"/tils/vim-regex-word-boundaries/","summary":"Today while writing a Vim regex to change every instance of it (ignoring larger matches like itemized), we stumbled upon Vim regex word boundary matching.\nGiven this file:\nfoo foobar The following Vim …","title":"Vim Regex Word Boundaries"},{"content":"What is a skill one could learn to set themselves apart from other entry-level programmers? Testing.\nThe web frameworks of today all have mature test libraries. Hashrocket\u0026amp;rsquo;s default tool is Ruby on Rails, and testing is a fundamental part of that community. Everything we do is test-driven.\nNew apprentices who come into any consultancy will have a lot to learn. At Hashrocket we use a pretty customized setup of Vim and Tmux, and have a ton of aliases and shorthands that can be hard to pick up. Our apprentices pair program with full-time developers, on real projects. It can be daunting at …","permalink":"/posts/learn-to-test/","summary":"What is a skill one could learn to set themselves apart from other entry-level programmers? Testing.\n","title":"Learn to Test"},{"content":"I\u0026amp;rsquo;ve been learning and using Elixir lately, and loving it. More than once, I\u0026amp;rsquo;ve been asked some version of the question: why are you learning Elixir? What makes this language unique? I thought I\u0026amp;rsquo;d take a moment and try to distill my rationale.\nThere are two main points: Elixir solves problems, and it\u0026amp;rsquo;s aesthetically interesting to me as a language.\nReal Problem Solving Elixir is written in Erlang, a language for telephony. Erlang is thus optimized for two things:\nConcurrency: must be able to run on multiple machines at once Uptime: phone call customers won\u0026amp;rsquo;t …","permalink":"/posts/why-elixir/","summary":"I\u0026rsquo;ve been learning and using Elixir lately, and loving it. More than once, I\u0026rsquo;ve been asked some version of the question: why are you learning Elixir? What makes this language unique? I …","title":"Why Elixir?"},{"content":"Sigils are a mechanism for working with textual representations in Elixir. If you\u0026amp;rsquo;ve ever made an array of Strings in Ruby with %w(), the API is similar. A neat feature of sigils is that we can make custom variants, or override existing Kernel variants. The latter is generally discouraged.\nCreating a Sigil Here\u0026amp;rsquo;s some code I wrote for an exercise, defining a new sigil, u:\ndefmodule SigilWriter do @moduledoc \u0026amp;#34;\u0026amp;#34;\u0026amp;#34; Provides a module for creating new sigils. \u0026amp;#34;\u0026amp;#34;\u0026amp;#34; @doc \u0026amp;#34;\u0026amp;#34;\u0026amp;#34; Defines the new sigil function for `u`. \u0026amp;#34;\u0026amp;#34;\u0026amp;#34; def sigil_u(content, …","permalink":"/posts/writing-elixir-sigils/","summary":"Sigils are a mechanism for working with textual representations in Elixir. If you\u0026rsquo;ve ever made an array of Strings in Ruby with %w(), the API is similar. A neat feature of sigils is that we can …","title":"Writing Elixir Sigils"},{"content":"Many pull requests go through a cycle: programmer opens pull request, maintainer gives feedback, programmer makes changes, repeat until ready to merge, maintainer merges. Prior to the merge, the pull request can be messy, full of reverts, fixups, and WIP commits. In the end, those commits are noise. We can tell a better story by squashing the branch.\nConsider this Git branch history:\n* 724d574 (HEAD -\u0026amp;gt; issue/38) Good to merge! * e2af5ac Refactoring the previous commit * 3e36475 Test are passing here * ea479cb Revert \u0026amp;#34;Ooops\u0026amp;#34; * 77aee1d Ooops * 4c7ead2 Test breaker The final commit at …","permalink":"/posts/squash-your-pr/","summary":"Many pull requests go through a cycle: programmer opens pull request, maintainer gives feedback, programmer makes changes, repeat until ready to merge, maintainer merges. Prior to the merge, the pull …","title":"How and Why to Squash Your Pull Request"},{"content":"I\u0026amp;rsquo;m learning Elixir, and today as an exercise, I was challenged to recreate Enum.sum using recursion. Here\u0026amp;rsquo;s my solution.\nFor those new to the language, Enum.sum iterates over a list and returns the sum. Here\u0026amp;rsquo;s an example:\niex(0)\u0026amp;gt; Enum.sum([1,2,3,4,5]) 15 Here is my implementation:\ndefmodule MyList do @moduledoc \u0026amp;#34;\u0026amp;#34;\u0026amp;#34; Defines the list struct and functions. \u0026amp;#34;\u0026amp;#34;\u0026amp;#34; @doc \u0026amp;#34;\u0026amp;#34;\u0026amp;#34; Add all the items in a list together. \u0026amp;#34;\u0026amp;#34;\u0026amp;#34; def sum(list) do sum(list, 0) end defp sum([], count) do count end defp sum([h|t], count) do sum(t, count + h) end end …","permalink":"/posts/recreating-enumsum/","summary":"I\u0026rsquo;m learning Elixir, and today as an exercise, I was challenged to recreate Enum.sum using recursion. Here\u0026rsquo;s my solution.\n","title":"Recreating Elixir's Enum.sum"},{"content":"Recently a friend asked me this question about server-side sorting in Rails. Here\u0026amp;rsquo;s the question and my answer.\nThe question:\nI\u0026amp;rsquo;m working on an app that displays movies. The index page simply lists all movies in a table %table#movies in HAML. There is a clickable \u0026amp;lsquo;Movie Title\u0026amp;rsquo; header above the list of movies and when clicked, the page should reload but in alphabetical order. Therefore I assume I need to use .order(:title) somewhere in the app. I was able to simply order all movies when the page loads by doing the following in movies_controller.rb:\ndef index @movies = …","permalink":"/posts/server-side-sorting/","summary":"Recently a friend asked me this question about server-side sorting in Rails. Here\u0026rsquo;s the question and my answer.\n","title":"Server Side Sorting in Ruby"},{"content":"After a recent talk I gave that included VimScript, an attendee asked a question about the differences between keyword, match, and region following syn in a Vim syntax highlighting file. Here\u0026amp;rsquo;s my answer.\nThese three short chapters from \u0026amp;lsquo;Learn VimScript the Hard Way\u0026amp;rsquo; help explain:\nChapter 45 Chapter 46 Chapter 47 Here\u0026amp;rsquo;s the short version:\nkeyword assigns a variable name directly to a word or multiple words match assigns a variable name to a Vim regex region assigns a variable name to something between a \u0026amp;ldquo;start\u0026amp;rdquo; pattern and an \u0026amp;ldquo;end\u0026amp;rdquo; pattern For …","permalink":"/posts/keyword-match-and-region-in-vimscript/","summary":"After a recent talk I gave that included VimScript, an attendee asked a question about the differences between keyword, match, and region following syn in a Vim syntax highlighting file. Here\u0026rsquo;s …","title":"Keyword, Match, and Region in VimScript"},{"content":"Tagging releases with Git is a good idea. In case your tags get off track, here is how you delete a Git tag locally and on a remote:\n$ git tag -d abc $ git push origin :refs/tags/abc To git@github.com:hashrocket/hr-til - [deleted] abc It gets trickier if you\u0026amp;rsquo;re using Semantic Versioning, which includes dots in the tag name. The above won\u0026amp;rsquo;t work for v16.0.0. This will:\n$ git tag -d v16.0.0 $ git push origin :v16.0.0 To git@github.com:hashrocket/hr-til - [deleted] v16.0.0 ","permalink":"/tils/delete-remote-git-tags/","summary":"Tagging releases with Git is a good idea. In case your tags get off track, here is how you delete a Git tag locally and on a remote:\n$ git tag -d abc $ git push origin :refs/tags/abc To …","title":"Delete Remote Git Tags"},{"content":"A while back I tackled the following programming challenge:\nFrom One End to the Other: Find the smallest possible (positive) integer that ends in a six such that if that six is removed and placed in front of the remaining digits of the number, the resulting number will be four times as large as the original.\nMy solution is below, and here:\nhttps://gist.github.com/jwworth/5a5b3647a44b9a782896\nI like that this solution is relatively fast, at 0.36 seconds. I like that it doesn\u0026amp;rsquo;t imply what the answer might be by having a ceiling to the search— the code will run forever if my conditional is …","permalink":"/posts/from-one-end-to-the-other-in-ruby/","summary":"A while back I tackled the following programming challenge:\nFrom One End to the Other: Find the smallest possible (positive) integer that ends in a six such that if that six is removed and placed in …","title":"From One End to the Other in Ruby"},{"content":"A few weeks back I tackled the following programming challenge from my colleague Josh Branchaud:\nVim Buffer\nI open up a new Vim buffer and type all the numbers 1 to 10,000, separated by spaces. Then, my cat walks on the keyboard and somehow activates a substitution command that replaces all the \u0026amp;lsquo;0’ digits (zeros) with spaces. If I now sum up all the numbers in the buffer, as delineated by spaces, what is the total?\nSolution Here\u0026amp;rsquo;s my solution, also available as a Gist.\ndef vim_buffer(start = 1, limit) (start..limit).flat_map do |num| num.to_s.gsub(\u0026amp;#39;0\u0026amp;#39;, \u0026amp;#39; \u0026amp;#39;).split …","permalink":"/posts/vim-buffer-problem/","summary":"A few weeks back I tackled the following programming challenge from my colleague Josh Branchaud:\nVim Buffer\nI open up a new Vim buffer and type all the numbers 1 to 10,000, separated by spaces. Then, …","title":"Vim Buffer Problem"},{"content":"In my never-ending quest to better summarize my work at the end of the day using computers, I discovered today the Git --author flag. It works like this:\n$ glg --since=midnight --author=dev+jwworth+mikechau@hashrocket.com * 4ba91a8 (HEAD, origin/checkout, checkout) Add guard for manual entry of employee discount * 3a4e4c9 Seed a coupon and code and auto-apply in preview * cb1adee Add discount ... The alias glg is discussed here.\nI use this when multiple developers or teams are committing throughout the day to the same repository, to disambiguate our work from others. Ready to paste into your …","permalink":"/tils/git-log-with-authors/","summary":"In my never-ending quest to better summarize my work at the end of the day using computers, I discovered today the Git --author flag. It works like this:\n$ glg --since=midnight …","title":"Git Log With Authors"},{"content":"Today I learned a great way to count the links in a README or link collection. Vim Regex!\n:%s/- \\[//n In Vimspeak:\nOn every line, find all of the times where - [ (the beginning of a Markdown bulleted hyperlink) occurs, count them, and report the number of matches.\nSee :help substitute in Vim for more information.\nh/t Josh Branchaud\n","permalink":"/tils/count-links-in-a-markdown-file/","summary":"Today I learned a great way to count the links in a README or link collection. Vim Regex!\n:%s/- \\[//n In Vimspeak:\nOn every line, find all of the times where - [ (the beginning of a Markdown bulleted …","title":"Count Links in a Markdown File"},{"content":"Today I learned how to query the size of a Postgres array. There are (at least) two methods that work.\nThe first is array_length. This requires you to know the array dimension you seek; a useful filter, although most of the time this will probably be 1:\nhr-til_development=# select title from posts hr-til_development=# where array_length(slack_notified_at_likes_threshold, 1) = 1; title ------------------------------------------- Because Javascript Percent Notation DIY Grids for Designing UI in Illustrator (3 rows) When the dimension is 1, a more terse solution is cardinality: …","permalink":"/tils/query-the-size-of-postgres-array/","summary":"Today I learned how to query the size of a Postgres array. There are (at least) two methods that work.\nThe first is array_length. This requires you to know the array dimension you seek; a useful …","title":"Query the Size of Postgres Array"},{"content":"We can add a PostgreSQL array column with a default empty array using:\nadd column things integer[] not null default \u0026amp;#39;{}\u0026amp;#39;; Today I added an array of integers to a PostgreSQL table, and like all such migrations, it will default to null for each new record.\nThis was a problem, because I wanted to use Rails built-in PostgreSQL array support to make decisions based on that data. Ruby array methods like include? will raise NoMethodError: undefined method \u0026#39;include?\u0026#39; for nil:NilClass if that array is ever null / nil, which it is by default.\nThis led me to learn how to set the default value to …","permalink":"/tils/default-to-empty-array-in-postgres/","summary":"We can add a PostgreSQL array column with a default empty array using:\nadd column things integer[] not null default \u0026#39;{}\u0026#39;; ","title":"PostgreSQL Empty Array as Default Value"},{"content":"This afternoon my pair and I spent quite a while on a subtle bug. The culprit? Variable hoisting.\nTake this file:\nclass Test def self.hoist require \u0026amp;#39;pry\u0026amp;#39;; binding.pry; bar = 1 end end Test.hoist When we hit the PRY debugger, what will the value of bar be? I would have thought it would raise NameError, because bar has seemingly not yet been defined.\nWrong:\n$ ruby test.rb From: /test.rb @ line 4 Test.hoist: 2: def self.hoist 3: require \u0026amp;#39;pry\u0026amp;#39;; binding.pry; =\u0026amp;gt; 4: bar = 1 5: end [1] pry(Test)\u0026amp;gt; bar =\u0026amp;gt; nil When Ruby parses a file, it \u0026amp;lsquo;hoists\u0026amp;rsquo; each variable to the …","permalink":"/tils/variable-hoisting-in-ruby/","summary":"This afternoon my pair and I spent quite a while on a subtle bug. The culprit? Variable hoisting.\nTake this file:\nclass Test def self.hoist require \u0026#39;pry\u0026#39;; binding.pry; bar = 1 end end …","title":"Variable Hoisting in Ruby"},{"content":"Today I found a great command in Pathogen.vim, :Helptags. This is a riff off :helptags {dir}, a Vim feature that builds helptags for directory {dir}.\n:Helptags does the same for all the directories in your runtimepath, which defaults on Unix and Mac OS X to:\n\u0026amp;#34;$HOME/.vim, $VIM/vimfiles, $VIMRUNTIME, $VIM/vimfiles/after, $HOME/.vim/after\u0026amp;#34; The use case here is when you\u0026amp;rsquo;ve just loaded a new plugin into your Vim bundle, you open up Vim, and :h {my_new_plugin} isn\u0026amp;rsquo;t \u0026amp;lsquo;helping\u0026amp;rsquo; you out.\n","permalink":"/tils/get-help-with-pathogen/","summary":"Today I found a great command in Pathogen.vim, :Helptags. This is a riff off :helptags {dir}, a Vim feature that builds helptags for directory {dir}.\n:Helptags does the same for all the directories in …","title":"Get help with Pathogen"},{"content":"Have you ever wondered about those secret keys found in config/secrets.yml of your Rails app? The comments generated in that file describe the keys as \u0026amp;lsquo;used for verifying the integrity of signed cookies.\u0026amp;rsquo;\nGreat\u0026amp;hellip; but what if they become compromised? Or we need to change them? We can generate new ones.\nRails provides rake secret for just this purpose.\nThe source code is here. It\u0026amp;rsquo;s pretty simple; the code simply requires SecureRandom and spits out a string. If you want to be really clever, you can pipe the string directly into your Vim buffer for the config file, with :.! …","permalink":"/tils/generate-a-rails-secret-key/","summary":"Have you ever wondered about those secret keys found in config/secrets.yml of your Rails app? The comments generated in that file describe the keys as \u0026lsquo;used for verifying the integrity of signed …","title":"Generate a Rails Secret Key"},{"content":"This week Josh Branchaud wrote about show-models in Pry-rails:\nhttp://til.hashrocket.com/posts/62f61ee06f-show-rails-models-with-pry\nThis method has two nice variants. show-model User shows you that model, useful in a large database.\nshow-models also accepts --grep for searching:\n[1] pry(main)\u0026amp;gt; show-models --grep published Post id: integer title: string published_at: datetime # \u0026amp;#39;published\u0026amp;#39; is highlighted ","permalink":"/tils/show-model-and-grep-with-pryrails/","summary":"This week Josh Branchaud wrote about show-models in Pry-rails:\nhttp://til.hashrocket.com/posts/62f61ee06f-show-rails-models-with-pry\nThis method has two nice variants. show-model User shows you that …","title":"Show Model and Grep with Pry-rails"},{"content":"ActiveRecord provides default_scope, which you can use to control how records are returned. An example:\n# app/model/chapter.rb class Chapter \u0026amp;lt; ActiveRecord::Base default_scope { order(:title) } end Now, instead of this (ordered by ID):\nChapter.all.pluck(:title).take(5) =\u0026amp;gt; [\u0026amp;#34;BALINESE CAUSE\u0026amp;#34;, \u0026amp;#34;BUILDING RAT\u0026amp;#34;, \u0026amp;#34;TAIL\u0026amp;#34;, \u0026amp;#34;LILAC MERCURY\u0026amp;#34;, \u0026amp;#34;PUMP SQUARE\u0026amp;#34;] We get this:\nChapter.all.pluck(:title).take(5) =\u0026amp;gt; [\u0026amp;#34;AFGHANISTAN COAT\u0026amp;#34;, \u0026amp;#34;ALCOHOL GONG\u0026amp;#34;, \u0026amp;#34;ANTARCTICA BULL\u0026amp;#34;, \u0026amp;#34;ASPARAGUS\u0026amp;#34;, \u0026amp;#34;BALINESE CAUSE\u0026amp;#34;] Beware, this decision can …","permalink":"/tils/set-a-default-scope/","summary":"ActiveRecord provides default_scope, which you can use to control how records are returned. An example:\n# app/model/chapter.rb class Chapter \u0026lt; ActiveRecord::Base default_scope { order(:title) } end …","title":"Set a Default Scope"},{"content":"Vim has excellent commands for changing the contents of parentheses (ci(), brackets (ci[), and squiggly braces (ci{) (as well as single quotes, double quotes, backticks, etc.). But what about tags?\nPlace your cursor anywhere inside the tags, type cit in Visual mode, and this:\n\u0026amp;lt;div\u0026amp;gt;Begone\u0026amp;lt;/div\u0026amp;gt; Becomes:\n\u0026amp;lt;div\u0026amp;gt;\u0026amp;lt;/div\u0026amp;gt; d works too, but I prefer c because it puts you in Insert mode at the end of the opening tag, ready to type.\nCheck out :h v_it for more information.\nh/t Josh Branchaud\n","permalink":"/tils/change-inner-tag-block/","summary":"Vim has excellent commands for changing the contents of parentheses (ci(), brackets (ci[), and squiggly braces (ci{) (as well as single quotes, double quotes, backticks, etc.). But what about tags? …","title":"Change Inner Tag Block"},{"content":"Haml\u0026amp;rsquo;s design makes HTML and Ruby tricky to mix inline. But we have Haml helper methods to make it easier.\nHere is succeed in action:\nDeveloper at = succeed \u0026amp;#39;, \u0026amp;#39; do = link_to \u0026amp;#34;Hashrocket\u0026amp;#34;, \u0026amp;#34;https://hashrocket.com/\u0026amp;#34; This becomes:\nDeveloper at \u0026amp;lt;a href=\u0026amp;#34;https://hashrocket.com/\u0026amp;#34;\u0026amp;gt;Hashrocket\u0026amp;lt;/a\u0026amp;gt;, Notice the comma at the end, outside of the a tag?\nprecede and surround work in a similar fashion, and do what we\u0026amp;rsquo;d expect.\nhttp://haml.info/docs/yardoc/file.REFERENCE.html#succeed\n","permalink":"/tils/succeed-precede-and-surround-in-haml/","summary":"Haml\u0026rsquo;s design makes HTML and Ruby tricky to mix inline. But we have Haml helper methods to make it easier.\nHere is succeed in action:\nDeveloper at = succeed \u0026#39;, \u0026#39; do = link_to …","title":"Succeed, Precede, and Surround in Haml"},{"content":"Vim has commands built-in to increment and decrement numbers: CTRL-A and CTRL-X, respectively.\nCombining this with a macro was a technique we tried today to build a large database migration. We ended up finding a more efficient solution, but it\u0026amp;rsquo;s still a very magical key combination.\nCheck out :help CTRL-A for more info.\nh/t Josh Branchaud\n","permalink":"/tils/increment-and-decrement-numbers/","summary":"Vim has commands built-in to increment and decrement numbers: CTRL-A and CTRL-X, respectively.\nCombining this with a macro was a technique we tried today to build a large database migration. We ended …","title":"Increment and Decrement Numbers"},{"content":"We had a testing issue this week when a button\u0026amp;rsquo;s text save changes! was rendering as SAVE CHANGES!, due to the CSS property text-transform: uppercase. How do you test that?\nOne technique is to use RSpec\u0026amp;rsquo;s match method, and include case insensitivity with i (explored earlier here):\n\u0026amp;gt; button_text = \u0026amp;#34;foo\u0026amp;#34; =\u0026amp;gt; \u0026amp;#34;foo\u0026amp;#34; \u0026amp;gt; expect(button_text).to match(\u0026amp;#34;FOO\u0026amp;#34;) RSpec::Expectations::ExpectationNotMetError: expected \u0026amp;#34;foo\u0026amp;#34; to match \u0026amp;#34;FOO\u0026amp;#34; # stuff \u0026amp;gt; expect(button_text).to match(/FOO/i) =\u0026amp;gt; true We ultimately hard coded the expectation to …","permalink":"/tils/expect-a-caseinsensitive-match/","summary":"We had a testing issue this week when a button\u0026rsquo;s text save changes! was rendering as SAVE CHANGES!, due to the CSS property text-transform: uppercase. How do you test that?\nOne technique is to …","title":"Expect a Case-Insensitive Match"},{"content":"Today I found a way to assert that an edit form\u0026amp;rsquo;s inputs include a record\u0026amp;rsquo;s saved data. I think it strikes a good balance between broad and narrow scope.\n# spec/features/user_edits_kit_spec.rb within \u0026amp;#39;form\u0026amp;#39; do expect(page).to have_selector(\u0026amp;#34;input[value=\u0026amp;#39;Default copy.\u0026amp;#39;]\u0026amp;#34;) end This asserts that some content is inside an input field in the form, rather than just anywhere on the page. You can narrow the scope as needed.\n","permalink":"/tils/testing-edit-forms/","summary":"Today I found a way to assert that an edit form\u0026rsquo;s inputs include a record\u0026rsquo;s saved data. I think it strikes a good balance between broad and narrow scope.\n# …","title":"Testing Edit Forms"},{"content":"Today I was cleaning up a test with an extra empty line at the top of the file, away from my Vim cursor. I wanted to delete it\u0026amp;hellip; without moving the cursor.\nIt seems like Vim almost lets you do this, with :[range]d. But it leaves the cursor on the deleted line, which isn\u0026amp;rsquo;t very magical.\nThis is the hack we found:\n:[range]d Then:\n\u0026amp;#39;\u0026amp;#39; \u0026#39;\u0026#39; returns the cursor to the last jump point.\nh/t Josh Branchaud\n","permalink":"/tils/delete-a-line-from-another-line/","summary":"Today I was cleaning up a test with an extra empty line at the top of the file, away from my Vim cursor. I wanted to delete it\u0026hellip; without moving the cursor.\nIt seems like Vim almost lets you do …","title":"Delete a Line From Another Line"},{"content":"Today I used Ruby\u0026amp;rsquo;s Kernel::abort for the first time. It\u0026amp;rsquo;s one of those incredibly useful methods I can\u0026amp;rsquo;t believe I\u0026amp;rsquo;ve never seen before.\nObserve:\n# abort.rb {1} def no_message abort end def message abort(\u0026amp;#39;Process terminated\u0026amp;#39;) end $ irb 2.2.2 :001 \u0026amp;gt; require \u0026amp;#39;./abort\u0026amp;#39; =\u0026amp;gt; true 2.2.2 :002 \u0026amp;gt; no_message $ irb {1} 2.2.2 :001 \u0026amp;gt; require \u0026amp;#39;./abort\u0026amp;#39; =\u0026amp;gt; true 2.2.2 :002 \u0026amp;gt; message Process terminated $ Use it to bail from a script.\nhttp://ruby-doc.org/core-2.1.2/Kernel.html#method-i-abort\n","permalink":"/tils/rubys-kernel-abort/","summary":"Today I used Ruby\u0026rsquo;s Kernel::abort for the first time. It\u0026rsquo;s one of those incredibly useful methods I can\u0026rsquo;t believe I\u0026rsquo;ve never seen before.\nObserve:\n# abort.rb {1} def no_message …","title":"Ruby's Kernel::abort"},{"content":"Today I learned that I have thirty-nine Postgres databases on my computer, after running this command inside psql:\n\\l ; Each one is small, but I don\u0026amp;rsquo;t like to carrying around old data. I ended up dropping nine of them, with:\ndrop database foo_development; For a lighter storage and cognitive load.\n","permalink":"/tils/cleanup-postgres-databases/","summary":"Today I learned that I have thirty-nine Postgres databases on my computer, after running this command inside psql:\n\\l ; Each one is small, but I don\u0026rsquo;t like to carrying around old data. I ended …","title":"Cleanup PostgreSQL Databases"},{"content":"This week I learned that attr_reader is more performant than a simple getter method.\nHere’s a gist from five years ago where Aaron Patterson explains:\nhttps://gist.github.com/pjb3/629716\nI ran that same benchmarking script today on Ruby 2.2.3. The gap has narrowed, but still exists:\nruby-2.2.3 Rehearsal ----------------------------------------------- method 0.070000 0.000000 0.070000 ( 0.074840) attr_reader 0.050000 0.000000 0.050000 ( 0.052603) -------------------------------------- total: 0.120000sec user system total real method 0.070000 0.000000 0.070000 ( 0.072229) attr_reader 0.050000 …","permalink":"/tils/accessor-performance-gap/","summary":"This week I learned that attr_reader is more performant than a simple getter method.\nHere’s a gist from five years ago where Aaron Patterson explains:\nhttps://gist.github.com/pjb3/629716\nI ran that …","title":"Accessor Performance Gap"},{"content":"I like to read commit logs.\nToday I wanted to see the first commit on a project. Here\u0026amp;rsquo;s what I used:\ngit rev-list --max-parents=0 HEAD Show me the commits that led to HEAD in reverse chronological order; then limit that list to the commits with no parent.\nHere\u0026amp;rsquo;s a small modification, to show the entire commit rather than the SHA alone:\ngit show $(git rev-list --max-parents=0 HEAD) ","permalink":"/tils/the-alpha-commit/","summary":"I like to read commit logs.\nToday I wanted to see the first commit on a project. Here\u0026rsquo;s what I used:\ngit rev-list --max-parents=0 HEAD Show me the commits that led to HEAD in reverse …","title":"The Alpha Commit"},{"content":"Today I found a useful method in ActiveRecord, restore_attributes.\nThis method restores attributes on a dirty model. Use it when you are hacking in the Rails console and want to quickly return to a clean slate.\nDirty the record:\n[1] pry(main)\u0026amp;gt; p = Post.first Post Load (0.4ms) SELECT \u0026amp;#34;posts\u0026amp;#34;.* FROM \u0026amp;#34;posts\u0026amp;#34; ORDER BY \u0026amp;#34;posts\u0026amp;#34;.\u0026amp;#34;id\u0026amp;#34; ASC LIMIT 1 =\u0026amp;gt; #\u0026amp;lt;Post:0x007f8462b09eb8 id: 106, title: \u0026amp;#34;Hello World!\u0026amp;#34;, created_at: Sun, 09 Feb 2014 17:15:01 CST -06:00, url_slug: \u0026amp;#34;hello-world\u0026amp;#34;\u0026amp;gt; [2] pry(main)\u0026amp;gt; p.url_slug = \u0026amp;#39;foobar\u0026amp;#39; =\u0026amp;gt; …","permalink":"/tils/rails-restoreattributes/","summary":"Today I found a useful method in ActiveRecord, restore_attributes.\nThis method restores attributes on a dirty model. Use it when you are hacking in the Rails console and want to quickly return to a …","title":"Rails restore_attributes"},{"content":"I\u0026amp;rsquo;m a huge fan of Vim buffers. I try to have my buffer list roughly mirror the files I am currently holding in my brain.\nThe BufExplorer Vim plugin helps, and is included in the Hashrocket Dotmatrix. Today I learned to use the command \\bs, which opens a colorful buffer list that you can navigate with Vim directions.\nhttps://github.com/jlanzarotta/bufexplorer\n","permalink":"/tils/explore-buffers-with-bufexplorer/","summary":"I\u0026rsquo;m a huge fan of Vim buffers. I try to have my buffer list roughly mirror the files I am currently holding in my brain.\nThe BufExplorer Vim plugin helps, and is included in the Hashrocket …","title":"Explore Buffers with BufExplorer"},{"content":"The rails generate (g) command is useful for quickly creating the building blocks of a Rails app. Until you misspell or otherwise mess up a command:\n% rails g model blurg_post invoke active_record create db/migrate/20151006162244_create_blurg_posts.rb create app/models/blurg_post.rb invoke rspec create spec/models/blurg_post_spec.rb \u0026amp;lsquo;Blurg post\u0026amp;rsquo;? I meant \u0026amp;lsquo;blog post\u0026amp;rsquo;. Luckily, we can remove those auto-generated files as fast as we created them, with destroy (d).\n% rails d model blurg_post invoke active_record remove db/migrate/20151006162244_create_blurg_posts.rb remove …","permalink":"/tils/rails-destroy/","summary":"The rails generate (g) command is useful for quickly creating the building blocks of a Rails app. Until you misspell or otherwise mess up a command:\n% rails g model blurg_post invoke active_record …","title":"Rails destroy"},{"content":"At the end of each day, I try to record what I did, to jog my memory during the next morning\u0026amp;rsquo;s standup. This is a helpful aid:\ngit log --since=\u0026amp;#34;24 hours ago\u0026amp;#34; I SSH into my work machine and run this in my project\u0026amp;rsquo;s root directory. Combined with an alias from the Hashrocket Dotmatrix, glg (git log --graph --oneline --decorate --color --all), I get a terse summary of the day\u0026amp;rsquo;s work, ready to be pasted into your note-taking or project management tool of choice:\n$ glg --since=\u0026amp;#34;24 hours ago\u0026amp;#34; * 7191b92 (HEAD, origin/master, origin/HEAD, master) Good changes * 3f4d61e …","permalink":"/tils/git-log-since/","summary":"At the end of each day, I try to record what I did, to jog my memory during the next morning\u0026rsquo;s standup. This is a helpful aid:\ngit log --since=\u0026#34;24 hours ago\u0026#34; I SSH into my work machine …","title":"Git Log since"},{"content":"It\u0026amp;rsquo;s important to fetch Ruby environmental variables rather than directly referencing them.\nThis code depends on \u0026amp;lsquo;secret_key\u0026amp;rsquo;:\nkey = ENV[\u0026amp;#39;secret_key\u0026amp;#39;] When \u0026amp;lsquo;secret_key\u0026amp;rsquo; is not set, \u0026amp;lsquo;key\u0026amp;rsquo; will be assigned nil, which is an object.\nLater, a call to key.upcase will produce a NoMethodError, because Ruby doesn\u0026amp;rsquo;t know how to uppercase nil.\nThis is better:\nkey = ENV.fetch(\u0026amp;#39;secret_key\u0026amp;#39;) Now, when \u0026amp;lsquo;secret_key\u0026amp;rsquo; is not set, KeyError will be raised. The code fails fast.\nfetch takes a second argument as a default, which means you …","permalink":"/tils/fetch-your-environmental-variables/","summary":"It\u0026rsquo;s important to fetch Ruby environmental variables rather than directly referencing them.\nThis code depends on \u0026lsquo;secret_key\u0026rsquo;:\nkey = ENV[\u0026#39;secret_key\u0026#39;] When …","title":"Fetch Your Environmental Variables"},{"content":"\u0026amp;lsquo;Random\u0026amp;rsquo; numbers in Go don\u0026amp;rsquo;t always seem random. This is because the rand package defaults to a seed of 1.\nThat\u0026amp;rsquo;s great if you need a bunch of random numbers at the start of your program. Not great if you expect a different outcome each time you run the program.\nA solution is to seed rand with Unix time. Try it in the init() function:\npackage main import ( \u0026amp;#34;math/rand\u0026amp;#34; \u0026amp;#34;time\u0026amp;#34; ) func init() { rand.Seed(time.Now().UTC().UnixNano()) } ... ","permalink":"/tils/seeding-golangs-rand/","summary":"\u0026lsquo;Random\u0026rsquo; numbers in Go don\u0026rsquo;t always seem random. This is because the rand package defaults to a seed of 1.\nThat\u0026rsquo;s great if you need a bunch of random numbers at the start of …","title":"Seeding Golang's Rand"},{"content":"Want to build URLs with JavaScript? Try thewindow.location object.\nThis came from a React.js project we\u0026amp;rsquo;re working on. It uses ES6 template string interpolation, which isn\u0026amp;rsquo;t necessary but definitely is nice:\n`${window.location.protocol}//${window.location.host}/posts/${this.props.postID}/edit` Which becomes:\n\u0026amp;#34;https://secret-project.com/posts/42/edit\u0026amp;#34; ","permalink":"/tils/build-urls-with-window-location/","summary":"Want to build URLs with JavaScript? Try thewindow.location object.\nThis came from a React.js project we\u0026rsquo;re working on. It uses ES6 template string interpolation, which isn\u0026rsquo;t necessary but …","title":"Build URLs with Window Location"},{"content":"Using a Postgres table for caching? You might want to try making it unlogged.\nunlogged tables are not written to the write-ahead log, which makes them much faster. This also means they are not crash-safe, and are truncated when a crash or unclean shutdown occurs. For caching purposes, that\u0026amp;rsquo;s likely to be an acceptable tradeoff.\nDocumentation\n","permalink":"/tils/postgres-unlogged/","summary":"Using a Postgres table for caching? You might want to try making it unlogged.\nunlogged tables are not written to the write-ahead log, which makes them much faster. This also means they are not …","title":"Postgres Unlogged"},{"content":"Go has an interesting feature called iota. When declaring a list of constants, this keyword represents successive untyped integer constants.\nconst ( foo = iota // foo == 0 bar = iota // bar == 1 baz = iota // baz == 2 ) Anytime const is invoked, the counter resets.\nconst foo = iota // foo == 0 const bar = iota // bar == 0 This is a cool way to quickly define a list of integer constants, such as \u0026amp;rsquo;true\u0026amp;rsquo; and \u0026amp;lsquo;false\u0026amp;rsquo;, for later use.\n","permalink":"/tils/go-iota/","summary":"Go has an interesting feature called iota. When declaring a list of constants, this keyword represents successive untyped integer constants.\nconst ( foo = iota // foo == 0 bar = iota // bar == 1 baz = …","title":"Go iota"},{"content":"Ruby has a nice percent string literal for regular expressions, %r. It\u0026amp;rsquo;s like // but allows your regex to contain backslashes without escaping them.\nCheck it out:\n2.1.0 :001 \u0026amp;gt; \u0026amp;#39;http://google.com\u0026amp;#39;.gsub(/http:\\/\\/google.com/, \u0026amp;#39;fine\u0026amp;#39;) =\u0026amp;gt; \u0026amp;#34;fine\u0026amp;#34; 2.1.0 :002 \u0026amp;gt; \u0026amp;#39;http://google.com\u0026amp;#39;.gsub(%r{http://google.com}, \u0026amp;#39;better\u0026amp;#39;) =\u0026amp;gt; \u0026amp;#34;better\u0026amp;#34; As the example implies, this is useful when matching URLs.\nDocumentation\n","permalink":"/tils/ruby-regex-literal/","summary":"Ruby has a nice percent string literal for regular expressions, %r. It\u0026rsquo;s like // but allows your regex to contain backslashes without escaping them.\nCheck it out:\n2.1.0 :001 \u0026gt; …","title":"Ruby Regex Literal"},{"content":"Project announcement! Last night I built a Markov Chain-generated novel, titled Ceramic Nation.\nIt\u0026amp;rsquo;s presented as a robot named Ellis Champlin, who is diligently composing one chapter a day based on a corpus of classic public-domain novels and philosophy texts. Here is the site and source code.\nThis was an exercise in rapidly building an MVP, styling, testing, Dev Opsing, and Markov-chaining. I\u0026amp;rsquo;m curious what this bot will produce as the days go on, and am hoping for greatness. I plan to refine the logic slowly to produce better and better chapters, and rotate the source documents …","permalink":"/posts/ceramic-nation/","summary":"Project announcement! Last night I built a Markov Chain-generated novel, titled Ceramic Nation.\n","title":"Building a Markov-Chain MVP in Ruby"},{"content":"You can store a PostgreSQL query with prepare.\ndb=# prepare posts_search as select title from posts limit 5; PREPARE Call the method with execute.\ndb=# execute posts_search; title ----------------------------------- Hello World! My First Pull Request: HAML My First Pull Request: Sinatra My First Pull Request: Capistrano My First Pull Request: SASS (5 rows) Deallocate the query with deallocate, and you can set it again.\ndb=# deallocate posts_search; DEALLOCATE db=# prepare posts_search as select title from posts limit 10; PREPARE ","permalink":"/tils/prepare-execute/","summary":"You can store a PostgreSQL query with prepare.\ndb=# prepare posts_search as select title from posts limit 5; PREPARE Call the method with execute.\ndb=# execute posts_search; title …","title":"Prepare / Execute"},{"content":"Want to change database connections from inside psql? You can!\nPsql, the REPL for Postgres, has a useful meta-command called \\connect, or \\c. This lets you establish a new connection to a Postgres server, while closing the current connection.\nHere is the required format:\n\\c or \\connect [ dbname [ username ] [ host ] [ port ] ] | conninfo Only \\c [my_database] is required; omitted parameters are taken from the previous connection.\nDocumentation\n","permalink":"/tils/psql-connect/","summary":"Want to change database connections from inside psql? You can!\nPsql, the REPL for Postgres, has a useful meta-command called \\connect, or \\c. This lets you establish a new connection to a Postgres …","title":"Psql Connect"},{"content":"To maintain data integrity, certain actions like rake db:drop will not execute when there are active sessions on a Postgres database. You\u0026amp;rsquo;ll get an error like this:\nERROR: database \u0026amp;#34;database_name\u0026amp;#34; is being accessed by other users DETAIL: There are 2 other session(s) using the database. Sessions can be killed from the command line, but a safer route is to kill them with a database method. First, revoke all public access from the database:\nrevoke connect on database_name from public; Next, kill all processes on the database, other than your own connection:\nselect …","permalink":"/tils/terminate_database_connections/","summary":"To maintain data integrity, certain actions like rake db:drop will not execute when there are active sessions on a Postgres database. You\u0026rsquo;ll get an error like this:\nERROR: database …","title":"Terminate Database Connections"},{"content":"The Ruby keyword not and the unary ! aren\u0026amp;rsquo;t the same. They have a different precedence, and therefore are not interchangeable, despite seeming very similar.\nirb \u0026amp;gt; not 3 == 4 =\u0026amp;gt; true irb \u0026amp;gt; !3 == 4 =\u0026amp;gt; false The first example is evaluated as not (3 == 4), or not false, which is true. The second example is evaluated as (not 3) == 4, or false == 4, which is false.\nJust use !. It\u0026amp;rsquo;s terser, it\u0026amp;rsquo;s less ambiguous, and it can be overridden.\nRuby\u0026amp;rsquo;s and and or have similar gotchas, which I plan to write about here in the future.\n","permalink":"/tils/keyword-precedence/","summary":"The Ruby keyword not and the unary ! aren\u0026rsquo;t the same. They have a different precedence, and therefore are not interchangeable, despite seeming very similar.\nirb \u0026gt; not 3 == 4 =\u0026gt; true irb …","title":"Keyword Precedence"},{"content":"Ruby has a few interesting old keywords that use the underscore, or \u0026amp;lsquo;skid\u0026amp;rsquo;, to convey meta information about the file.\nHere they are in action:\n# test.rb puts __ENCODING__ puts __FILE__ puts __LINE__ __END__ Because this is after \u0026amp;#39;end\u0026amp;#39;, it should not be executed Running this file produces:\n$ ruby test.rb UTF-8 test.rb 3 Pretty cool!\n","permalink":"/tils/skid-keywords/","summary":"Ruby has a few interesting old keywords that use the underscore, or \u0026lsquo;skid\u0026rsquo;, to convey meta information about the file.\nHere they are in action:\n# test.rb puts __ENCODING__ puts __FILE__ …","title":"Skid Keywords"},{"content":"The not null constraint is a great way to ensure data integrity. If a Rails model validates_presence_of an attribute, that column should be not null in the database.\nRails has a special migration method for setting this constraint.\nchange_column_null :users, :mandatory_attribute, false You could also use the change_column method. The reason change_column_null is a better choice is that change_column requires you to state the type of the column; change_column_null does not.\nchange_column(table_name, column_name, type, options) change_column_null(table_name, column_name, null, default = nil) ","permalink":"/tils/change-column-null/","summary":"The not null constraint is a great way to ensure data integrity. If a Rails model validates_presence_of an attribute, that column should be not null in the database.\nRails has a special migration …","title":"Change Column Null"},{"content":"Metaprogramming! Today I created a variable event that calls a serializer unique to the value of event. Along the way I learned about the Rails methods classify and constantize.\nclassify takes a plural table name and returns a class name.\n\u0026amp;gt; \u0026amp;#39;some_things\u0026amp;#39;.classify =\u0026amp;gt; \u0026amp;#34;SomeThing\u0026amp;#34; constantize tries to find a constant with the name specified in the argument string.\n\u0026amp;gt; \u0026amp;#34;Module\u0026amp;#34;.constantize =\u0026amp;gt; Module \u0026amp;gt; \u0026amp;#34;Nothing\u0026amp;#34;.constantize NameError: uninitialized constant Nothing Here\u0026amp;rsquo;s a sample of how I used these methods today (on the \u0026amp;lsquo;Today I …","permalink":"/tils/classify-and-constantize/","summary":"Metaprogramming! Today I created a variable event that calls a serializer unique to the value of event. Along the way I learned about the Rails methods classify and constantize.\nclassify takes a …","title":"Classify and Constantize"},{"content":"The Rails find_or_create_by method is great for bringing something into existence only once. I like to use it for database seeds, so you get the basic objects you need to make your development site useable, but don\u0026amp;rsquo;t keep creating them over and over again.\nOne feature I recently learned is that this method takes a block.\nUser.find_or_create_by(first_name: \u0026amp;#39;Jake\u0026amp;#39;) do |user| user.last_name = \u0026amp;#39;Worth\u0026amp;#39; end This lets you find the object, and if it doesn\u0026amp;rsquo;t exist, create it with whatever attributes you need.\n","permalink":"/tils/find-or-create-by-with-block/","summary":"The Rails find_or_create_by method is great for bringing something into existence only once. I like to use it for database seeds, so you get the basic objects you need to make your development site …","title":"Find Or Create By With Block"},{"content":"The Vim command :e edits a file. Add a bang, :e!, to edit the current file \u0026amp;lsquo;always\u0026amp;rsquo;, discarding any changes to the current buffer.\nThis is useful if you rename a file with a new file extension (i.e. .txt to .rb). :e! reloads the file in Vim, picking up any customizations you have in your configuration files such as syntax highlighting.\n","permalink":"/tils/edit-the-current-file-always/","summary":"The Vim command :e edits a file. Add a bang, :e!, to edit the current file \u0026lsquo;always\u0026rsquo;, discarding any changes to the current buffer.\nThis is useful if you rename a file with a new file …","title":"Edit the Current File Always"},{"content":"I use HTTP Basic auth in non-production environments because it ships with Rails and is easy to implement. The first time I used it, I set it up like this:\n# app/controllers/application_controller.rb if ENV[\u0026amp;#39;http_basic_auth\u0026amp;#39;] http_basic_authenticate_with name: ENV[\u0026amp;#39;username\u0026amp;#39;], password: ENV[\u0026amp;#39;password\u0026amp;#39;] end I used environmental variables so that no credentials were hard-coded, and so I could toggle it without deploying.\nToday I learned you can also implement it like this:\n# app/controllers/application_controller.rb if creds = ENV[\u0026amp;#39;basic_auth_credentials\u0026amp;#39;] …","permalink":"/tils/multipurpose-environmental-variables/","summary":"I use HTTP Basic auth in non-production environments because it ships with Rails and is easy to implement. The first time I used it, I set it up like this:\n# app/controllers/application_controller.rb …","title":"Multipurpose Environmental Variables"},{"content":"Ruby\u0026amp;rsquo;s puts can take multiple arguments!\n2.2.0 :002 \u0026amp;gt; puts \u0026amp;#39;so\u0026amp;#39;, \u0026amp;#39;many\u0026amp;#39;, \u0026amp;#39;statements\u0026amp;#39;, \u0026amp;#39;to\u0026amp;#39;, \u0026amp;#39;puts\u0026amp;#39; so many statements to puts =\u0026amp;gt; nil ","permalink":"/tils/puts-multiple-lines/","summary":"Ruby\u0026rsquo;s puts can take multiple arguments!\n2.2.0 :002 \u0026gt; puts \u0026#39;so\u0026#39;, \u0026#39;many\u0026#39;, \u0026#39;statements\u0026#39;, \u0026#39;to\u0026#39;, \u0026#39;puts\u0026#39; so many statements to puts =\u0026gt; nil ","title":"Puts Multiple Lines"},{"content":"Ruby strings are truthy, and they evaluate to zero, unless they start with a number.\n\u0026amp;gt; \u0026amp;#39;nine to five\u0026amp;#39;.to_i =\u0026amp;gt; 0 \u0026amp;gt; \u0026amp;#39;9 to 5\u0026amp;#39;.to_i =\u0026amp;gt; 9 ","permalink":"/tils/truthy-strings/","summary":"Ruby strings are truthy, and they evaluate to zero, unless they start with a number.\n\u0026gt; \u0026#39;nine to five\u0026#39;.to_i =\u0026gt; 0 \u0026gt; \u0026#39;9 to 5\u0026#39;.to_i =\u0026gt; 9 ","title":"Truthy Strings"},{"content":"Vim can find and replace strings across files, just like other text editors. It\u0026amp;rsquo;s really (sort of) easy.\nFirst load all the files you want to change into the buffer with a splatted directory.\n:args path/to/files/*/* Then, execute the substitution.\n:argdo %s/old_string/new_string/ge | update The e flag is important; it tells Vim not to issue an error if the search pattern fails. This will prevent No Match errors from breaking the chain.\n","permalink":"/tils/find-and-replace-across-files/","summary":"Vim can find and replace strings across files, just like other text editors. It\u0026rsquo;s really (sort of) easy.\nFirst load all the files you want to change into the buffer with a splatted directory. …","title":"Find and Replace Across Files"},{"content":"Ruby\u0026amp;rsquo;s Enumerable class includes minmax, which returns a two element array with the minimum and maximum values of an enumerable.\n\u0026amp;gt; [1, 5, 10].minmax =\u0026amp;gt; [1, 10] \u0026amp;gt; [\u0026amp;#39;alpha\u0026amp;#39;, \u0026amp;#39;bravo\u0026amp;#39;, \u0026amp;#39;zulu\u0026amp;#39;].minmax =\u0026amp;gt; [\u0026amp;#34;alpha\u0026amp;#34;, \u0026amp;#34;zulu\u0026amp;#34;] ","permalink":"/tils/minmax/","summary":"Ruby\u0026rsquo;s Enumerable class includes minmax, which returns a two element array with the minimum and maximum values of an enumerable.\n\u0026gt; [1, 5, 10].minmax =\u0026gt; [1, 10] \u0026gt; [\u0026#39;alpha\u0026#39;, …","title":"Minmax"},{"content":"To call a Vimscript method in Vim, first source the file.\n:source ~/path/to/vimscript.vim Next, put the cursor over the method call, and run that line.\n:exec getline(\u0026amp;#34;.\u0026amp;#34;) ","permalink":"/tils/call-a-vimscript-method-in-vim/","summary":"To call a Vimscript method in Vim, first source the file.\n:source ~/path/to/vimscript.vim Next, put the cursor over the method call, and run that line.\n:exec getline(\u0026#34;.\u0026#34;) ","title":"Call a Vimscript Method in Vim"},{"content":"You can make Ruby regex matchers case insensitive with an i.\n2.1.0 :001 \u0026amp;gt; \u0026amp;#39;Expected output\u0026amp;#39; =~ /expected/ =\u0026amp;gt; nil 2.1.0 :002 \u0026amp;gt; \u0026amp;#39;Expected output\u0026amp;#39; =~ /expected/i =\u0026amp;gt; 0 This is useful when writing tests, when you care about a message, but are not interested in its exact format.\n","permalink":"/tils/case-insensitive-matchers/","summary":"You can make Ruby regex matchers case insensitive with an i.\n2.1.0 :001 \u0026gt; \u0026#39;Expected output\u0026#39; =~ /expected/ =\u0026gt; nil 2.1.0 :002 \u0026gt; \u0026#39;Expected output\u0026#39; =~ /expected/i =\u0026gt; 0 This is …","title":"Case Insensitive Matchers"},{"content":"Check the value of any Vim setting by adding a ? to the end of its name.\n# Validate spelling is turned off :set spell? =\u0026amp;gt; nospell # Validate incremental search is turned on :set incsearch? =\u0026amp;gt; :incsearch ","permalink":"/tils/current-value-of-a-setting/","summary":"Check the value of any Vim setting by adding a ? to the end of its name.\n# Validate spelling is turned off :set spell? =\u0026gt; nospell # Validate incremental search is turned on :set incsearch? =\u0026gt; …","title":"Current Value of a Setting"},{"content":"With Vim you can jump down any n amount of lines with n + j, and back up with n + k.\nAn alternate command is n + +, which jumps down to the first non-blank character, and n + -, which jumps back up to the first non-blank character.\n","permalink":"/tils/jump-to-the-first-non-blank-character/","summary":"With Vim you can jump down any n amount of lines with n + j, and back up with n + k.\nAn alternate command is n + +, which jumps down to the first non-blank character, and n + -, which jumps back up to …","title":"Jump to the First Non-Blank Character"},{"content":"To redirect a path in Rails:\n# config/routes.rb get \u0026amp;#34;/authors\u0026amp;#34; =\u0026amp;gt; redirect(\u0026amp;#34;/designers\u0026amp;#34;) This lets you take a difficult-to-type path name and expose it to your users as a simpler path name:\n# config/routes.rb get \u0026amp;#34;/login\u0026amp;#34; =\u0026amp;gt; redirect(\u0026amp;#34;/auth/google_oauth2\u0026amp;#34;) ","permalink":"/tils/redirect-a-path/","summary":"To redirect a path in Rails:\n# config/routes.rb get \u0026#34;/authors\u0026#34; =\u0026gt; redirect(\u0026#34;/designers\u0026#34;) This lets you take a difficult-to-type path name and expose it to your users as a simpler …","title":"Redirect a Path"},{"content":"Rails comes with HTTP Basic authentication built in. Adding this to a controller is how it\u0026amp;rsquo;s done:\n# app/controllers/application_controller.rb http_basic_authenticate_with name: \u0026amp;#39;name\u0026amp;#39;, password: \u0026amp;#39;password\u0026amp;#39; When you visit a URL requiring authentication, you can bypass the authentication screen by adding your username and password to the beginning of the URL. For localhost, this would look like:\nname:password@localhost:3000 ","permalink":"/tils/bypass-basic-auth/","summary":"Rails comes with HTTP Basic authentication built in. Adding this to a controller is how it\u0026rsquo;s done:\n# app/controllers/application_controller.rb http_basic_authenticate_with name: \u0026#39;name\u0026#39;, …","title":"Bypass Basic Auth"},{"content":"To save a snapshot of your current work in git, try this command:\ngit stash save \u0026amp;#34;snapshot: $(date)\u0026amp;#34; \u0026amp;amp;\u0026amp;amp; git stash apply \u0026amp;#34;stash@{0}\u0026amp;#34; This saves your current work in a timestamped stash, without removing it. In Hashrocket\u0026amp;rsquo;s dotmatrix this command is aliased to git snapshot.\n","permalink":"/tils/git-snapshot/","summary":"To save a snapshot of your current work in git, try this command:\ngit stash save \u0026#34;snapshot: $(date)\u0026#34; \u0026amp;\u0026amp; git stash apply \u0026#34;stash@{0}\u0026#34; This saves your current work in a …","title":"Git Snapshot"},{"content":"In Vim there are many ways to close a file. One of the best is :x, also know as :xit.\n:x writes and quits a file when changes have been made, like :wq, but with one less keystroke.\nAn important distinction:\n:x and :wq are similar, but not identical commands.\nThe difference is that :wq writes the current file and quits, as long as the file is not read-only and has a name, while :x only writes if changes have been made. So, if you were to generate a file with Vim, make no changes, and try to write and quit with :x, the file would not be written. Doing the same thing with :wq would write the …","permalink":"/tils/close-a-file/","summary":"In Vim there are many ways to close a file. One of the best is :x, also know as :xit.\n:x writes and quits a file when changes have been made, like :wq, but with one less keystroke.\nAn important …","title":"Close a File"},{"content":"One way to make a list nicer to read is to sort it. Vim comes with a command built in for just this purpose.\nHere is a snippet from a Gemfile:\ngem \u0026amp;#39;coffee-rails\u0026amp;#39;, \u0026amp;#39;~\u0026amp;gt; 4.1.0\u0026amp;#39; gem \u0026amp;#39;uglifier\u0026amp;#39;, \u0026amp;#39;\u0026amp;gt;= 1.3.0\u0026amp;#39; gem \u0026amp;#39;sass-rails\u0026amp;#39;, \u0026amp;#39;~\u0026amp;gt; 5.0\u0026amp;#39; gem \u0026amp;#39;puma\u0026amp;#39; gem \u0026amp;#39;gravatar_image_tag\u0026amp;#39; gem \u0026amp;#39;authem\u0026amp;#39; gem \u0026amp;#39;jquery-rails\u0026amp;#39; gem \u0026amp;#39;pg\u0026amp;#39; gem \u0026amp;#39;redcarpet\u0026amp;#39; gem \u0026amp;#39;rails_12factor\u0026amp;#39;, group: :production gem \u0026amp;#39;sdoc\u0026amp;#39;, \u0026amp;#39;~\u0026amp;gt; 0.4.0\u0026amp;#39;, group: :doc These gems might have been added during the development process. To …","permalink":"/tils/sort-alphabetically/","summary":"One way to make a list nicer to read is to sort it. Vim comes with a command built in for just this purpose.\nHere is a snippet from a Gemfile:\ngem \u0026#39;coffee-rails\u0026#39;, \u0026#39;~\u0026gt; 4.1.0\u0026#39; gem …","title":"Sort Alphabetically"},{"content":"Rails logs your server\u0026amp;rsquo;s activity, which is useful for development and debugging. However, often the server handles sensitive information that should not be logged.\nA few examples are authentication credentials, personal data, and financial information.\nTo prevent Rails from logging such data, add this to your application configuration:\nconfig.filter_parameters \u0026amp;lt;\u0026amp;lt; :sensitive_param When the named parameter is handled by the server, it will be logged as [FILTERED] instead of its actual value. Add this configuration by environment if you want to keep the parameters unfiltered in …","permalink":"/tils/parameters-filtering/","summary":"Rails logs your server\u0026rsquo;s activity, which is useful for development and debugging. However, often the server handles sensitive information that should not be logged.\nA few examples are …","title":"Parameters Filtering"},{"content":"In this example, we want to allow users to enter a twitter handle with or without an at-sign (\u0026amp;rsquo;@\u0026amp;rsquo;), just like Twitter, but save it without the at-sign. twitter_handle is already an attribute on the class.\nclass User \u0026amp;lt; ActiveRecord::Base def twitter_handle=(handle) write_attribute(:twitter_handle, handle.to_s.gsub(/^@/, \u0026amp;#39;\u0026amp;#39;)) end end ","permalink":"/tils/modify-attribute-on-set/","summary":"In this example, we want to allow users to enter a twitter handle with or without an at-sign (\u0026rsquo;@\u0026rsquo;), just like Twitter, but save it without the at-sign. twitter_handle is already an …","title":"Modify Attribute on Set"},{"content":"git reflog is a record of your actions in Git. With this command, you can undo almost any Git mistake.\n$ git reflog 4bd0090 HEAD@{0}: \u0026amp;lt;bad place\u0026amp;gt; 46bd839 HEAD@{1}: \u0026amp;lt;bad place\u0026amp;gt; 967e214 HEAD@{2}: \u0026amp;lt;last good place\u0026amp;gt; 46bd839 HEAD@{3}: \u0026amp;lt;good place\u0026amp;gt; 967e214 HEAD@{4}: \u0026amp;lt;good place\u0026amp;gt; $ git reset --hard HEAD@{2} ","permalink":"/tils/undo-a-git-mistake/","summary":"git reflog is a record of your actions in Git. With this command, you can undo almost any Git mistake.\n$ git reflog 4bd0090 HEAD@{0}: \u0026lt;bad place\u0026gt; 46bd839 HEAD@{1}: \u0026lt;bad place\u0026gt; 967e214 …","title":"Undo a Git Mistake"},{"content":":g/^\\s*#/d will remove comment lines from a file with Vim.\n","permalink":"/tils/delete-comments/","summary":":g/^\\s*#/d will remove comment lines from a file with Vim.\n","title":"Delete Comments"},{"content":"While in less (terminal pager program), h or H brings up a summary of commands.\n","permalink":"/tils/less-help/","summary":"While in less (terminal pager program), h or H brings up a summary of commands.\n","title":"Less Help"},{"content":"Recently, I was thinking about the N + 1 problem. N + 1 is a performance issue in a web application, where a method call unleashes a torrent of database queries.\nTake this method, which loads all users:\ndef index @users = User.all end Simple enough, but call some records in the view associated with each user, such as \u0026amp;lsquo;friends\u0026amp;rsquo; and \u0026amp;lsquo;reviews\u0026amp;rsquo;, and Rails builds a SQL query for each user, each user\u0026amp;rsquo;s friends, and each user\u0026amp;rsquo;s reviews. This is redundant, and with a big database, unacceptably slow. This is an N + 1 problem.\nAfter some reading, I decided that the …","permalink":"/posts/n-1-problem-and-n-plus/","summary":"Recently, I was thinking about the N + 1 problem. N + 1 is a performance issue in a web application, where a method call unleashes a torrent of database queries.\n","title":"The N + 1 Problem"},{"content":"Today I was trying to control what rendered on a page based on which controller action called it, and discovered the action_name method. It\u0026amp;rsquo;s located in actionview:\npry\u0026amp;gt; method(:action_name).source_location =\u0026amp;gt; [\u0026amp;#34;/Users/jake/.rvm/gems/ruby-2.2.3/gems/actionview-4.2.4/lib/action_view/helpers/controller_helper.rb\u0026amp;#34;, 10] Add this to your view code, and it you\u0026amp;rsquo;ll get the relevant action:\npry\u0026amp;gt; action_name =\u0026amp;gt; \u0026amp;#34;index\u0026amp;#34; Same partial, different context:\npry\u0026amp;gt; action_name =\u0026amp;gt; \u0026amp;#34;drafts\u0026amp;#34; This method would allow conditionals like this (written in HAML):\n- if …","permalink":"/tils/how-rails-action-name-works/","summary":"Today I was trying to control what rendered on a page based on which controller action called it, and discovered the action_name method. It\u0026rsquo;s located in actionview:\npry\u0026gt; …","title":"How Rails Action Name Works"},{"content":"What does \u0026amp;amp;: mean in Ruby? In this post, I\u0026amp;rsquo;ll explain.\nExplore a Rails app, and you might see something like this:\ndef self.names find(:all).collect(\u0026amp;amp;:name) end What is that \u0026amp;amp;:, you might be wondering? It\u0026amp;rsquo;s shorthand for this:\ndef self.names find(:all).collect { |p| p.name } end Often in Ruby, we are using a method that invokes a given block. The methods map, collect, and select are just three examples. To employ one of these methods, you might write something like this:\n[1,2,3,4,5].select { |num| num.odd? } #=\u0026amp;gt; [1,3,5] This is fine, but there is a more concise …","permalink":"/posts/ruby-symbolto_proc/","summary":"What does \u0026amp;: mean in Ruby? In this post, I\u0026rsquo;ll explain.\n","title":"Ruby Symbol#to_proc"},{"content":"I have one computer monitor on my desk. Not long ago, this was common, but today, many people have two, three, or four monitors. In this post, I\u0026amp;rsquo;d like to make the case that most professional programmers can use just one monitor.\nDisclaimer Hardware is a personal preference, and you can use any configuration you like. Some people may need more than one monitor for physical reasons. Some people may have jobs where secondary or tertiary displays perform a crucial function. I\u0026amp;rsquo;m sure there are valid exceptions here.\nSo, why write this post? I\u0026amp;rsquo;m writing to explain my longstanding …","permalink":"/posts/one-monitor/","summary":"I have one computer monitor on my desk. Not long ago, this was common, but today, many people have two, three, or four monitors. In this post, I\u0026rsquo;d like to make the case that most professional …","title":"The Case for One Computer Monitor"},{"content":"","permalink":"/talks/","summary":"","title":""}]