MicroPosts
- AI is not about working faster
This is why I care so much about the productivity boost I get from LLMs so much: it's not about getting work done faster, it's about being able to ship projects that I wouldn't have been able to justify spending time on at all.
- Exploring codebases with AI
Good LLMs are great at answering questions about code.
This is also very low stakes: the worst that can happen is they might get something wrong, which may take you a tiny bit longer to figure out. It's still likely to save you time compared to digging through thousands of lines of code entirely by yourself.
The trick here is to dump the code into a long context model and start asking questions. My current favorite for this is the catchily titled gemini-2.0-pro-exp-02-05, a preview of Google's Gemini 2.0 Pro which is currently free to use via their API.
- Write the dumbest possible code
The AI took me too literally 😅
- Render Html, Css, and Js from a GitHub gist
You can use
https://gistpreview.github.io/?GIST_ID
to render a gist: - Why I don't feel threatened by AI
My perspective on this as a developer who's been using these systems on a daily basis for a couple of years now is that I find that they enhance my value. I am so much more competent and capable as a developer because I've got these tools assisting me. I can write code in dozens of new programming languages that I never learned before.
But I still get to benefit from my 20 years of experience.
Take somebody off the street who's never written any code before and ask them to build an iPhone app with ChatGPT. They are going to run into so many pitfalls, because programming isn't just about can you write code - it's about thinking through the problems, understanding what's possible and what's not, understanding how to QA, what good code is, having good taste.
There's so much depth to what we do as software engineers.
I've said before that generative AI probably gives me like two to five times productivity boost on the part of my job that involves typing code into a laptop. But that's only 10 percent of what I do. As a software engineer, most of my time isn't actually spent with the typing of the code. It's all of those other activities.
The AI systems help with those other activities, too. They can help me think through architectural decisions and research library options and so on. But I still have to have that agency to understand what I'm doing.
So as a software engineer, I don't feel threatened. My most optimistic view of this is that the cost of developing software goes down because an engineer like myself can be more ambitious, can take on more things. As a result, demand for software goes up - because if you're a company that previously would never have dreamed of building a custom CRM for your industry because it would have taken 20 engineers a year before you got any results... If it now takes four engineers three months to get results, maybe you're in the market for software engineers now that you weren't before.
Accessibility and Gen AI Ep 6 - Simon Willison - Creator, Datasette
- Install an old version of a Brew formula
HOMEBREW_NO_INSTALL_FROM_API=1 brew tap --force homebrew/core cd $(brew --repository homebrew/core) git log --oneline --follow Formula/g/gdb.rb git checkout <sha> brew install ./Formula/g/gdb.rb
Source: https://gist.github.com/mike-myers-tob/9a6013124bad7ff074d3297db2c98247?permalink_comment_id=5407666#gistcomment-5407666
- AI allucination
- Human-AI Symbiosis
- Start with your idea and use AI on top
- Don't replace people or you lose wisdom: purpose, mission, culture
- A chess game with AI vs AI+Human would see the latter win 10x
- Automating with AI means you now have the time for the things you keep postponing (and not less work to do)
- How to develop creativity? Make people work on problems that can only be solved by being creative
- You need to know the domain to prompt properly an LLM
- The 70% AI problem
- AI is most useful to senior people
- AI gets you 70% of the way, but you need to know what you are doing to complete the remaining 30%
- Use AI to accelerate the known and/or explore possibilities
Source: https://addyo.substack.com/p/the-70-problem-hard-truths-about
- Better questions
Source: https://x.com/ShaanVP/status/1867650344060420418
- Rails console tricks
hash = { a: 1, b: "string", c: [1, 2, 3] } y hash --- :a: 1 :b: string :c: - 1 - 2 - 3
class User def initialize @ivar = 1 end def imet end def self.cmet end end # --- ls User User.methods: cmet #<Class:Object>#methods: yaml_tag User#methods: imet # --- ls User.new User#methods: imet instance variables: @ivar
app.root_path # or any other routes
- Bash script prelude
#!/usr/bin/env bash set -euo pipefail IFS=$'\n\t' set -x
Explanation:
#!/usr/bin/env bash
: interpret the subsequent lines withbash
set -e
: exit immediately with errorScript:
echo 1 not-existing-command echo 2
Out:
1 not-existing-command: command not found 2
Script:
set -e echo 1 not-existing-command echo 2
Out:
1 not-existing-command: command not found
set -u
: exit immediately with unbound variableScript:
echo 1 echo $NOT_EXISTING_VARIABLE echo 2
Out:
1 2
Script:
set -u echo 1 echo $NOT_EXISTING_VARIABLE echo 2
Out:
1 NOT_EXISTING_VARIABLE: unbound variable
set -o pipefail
: make errors fall through pipelinesScript:
not-existing-command | sort echo $?
Out:
not-existing-command: command not found 0
Script:
set -o pipefail not-existing-command | sort echo $?
Out:
not-existing-command: command not found 127
IFS=$'\n\t'
: Set input field separator (default: $' \n\t')Script:
string="1 2 3" for i in $string; do echo "$i" done IFS=$'\n\t' for i in $string; do echo "$i" done
Out:
1 2 3 1 2 3
Script:
array=( "a b" "c d" ) for x in ${array[@]}; do echo "$x" done IFS=$'\n\t' for x in ${array[@]}; do echo "$x" done
Out:
a b c d a b c d
set -x
: print lines as they are executedScript:
set -x echo 0 echo 1
Out:
+ echo 0 0 + echo 1 1
- Delete open Nvim buffers
List all open buffers:
:ls
Close all open buffers:
%bd!|e#|bd#
%bd!
foreach buffer deletee#
open the last buffer for editingbd#
delete the [No Name] buffer
Source: https://stackoverflow.com/questions/4545275/vim-close-all-buffers-but-this-one
- Global find/replace in Nvim
Fill the quickfix list with something like vim-grepper and:
cdo %s/BEFORE/AFTER/gc
cdo
executes what comes after for each entry in the quickfix list (or usecfdo
to run once per file). - AI bias
I shared in Why Good Solutions Block Better Ones why the first idea that comes to your mind may make you ignore other ones.
And here we are!
- form_with form object
The
form_with
helper accepts anActiveModel::Model
:<%= form_with model: TeamMemberForm.new do |f| %> <%= f.text_field :full_name %><br /> <%= f.email_field :email %><br /> <%= f.submit %> <% end %>
You can create a form object like the following:
class TeamMemberForm validates ... def self.model_name ActiveModel::Name.new(self, nil, 'TeamMember') end end
By using
ActiveModel::Name
, Rails will take care of fillingname
s andid
s as follows:<input type="text" name="team_member[full_name]" id="team_member_full_name"> <input type="email" name="team_member[email]" id="team_member_email">
So in the controller you can:
params.require(:team_member).permit(:full_name, :email)
- Dialog with Rails & Hotwire
At the end of
<body>
:<dialog data-controller="modal-dialog" class="tw-w-[calc(100%-0.5rem)] tw-max-w-xl tw-rounded-lg"> <div class="tw-p-8"> <button data-action="click->modal-dialog#close" class="tw-absolute tw-top-6 tw-right-8 tw-text-5xl tw-opacity-40 hover:tw-opacity-90 tw-select-none" >×</button> <%= turbo_frame_tag 'modal_dialog', data: { action: 'turbo:frame-load->modal-dialog#open' } %> </div> </dialog>
Stimulus controller:
import { Controller } from "@hotwired/stimulus"; export default class extends Controller { connect() { this.element.addEventListener("close", () => { this.element.classList.remove('open') }); } disconnect() { this.close(); } open() { this.element.showModal(); this.element.classList.add('open') } close() { this.element.close(); this.element.classList.remove('open') } }
CSS:
// This scrolls the page to the top, which is a problem if the // user opens the dialog not at the top of the page. // // The following would work but causes an horizontal layout // shift when the scrollbar disappears: // .no-scroll { // overflow: hidden; // } // // Keep an eye on the following thread for better solutions: // https://github.com/whatwg/html/issues/7732 body:has(dialog[open]) { overflow-y: scroll; position: fixed; width: 100%; } dialog { opacity: 0; transition: opacity 0.3s ease-out; box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.8); } dialog.open { opacity: 1; } dialog::backdrop { display: none; }
Button that opens the dialog:
<%= link_to( fa_icon('plus', text: 'Invite user'), new_dashboard_organization_team_member_path(@organization), class: '!tw-py-2 !tw-px-3 !tw-text-base btn btn-default', data: { turbo: true, turbo_frame: 'modal_dialog' } ) %>
Form that gets rendered into the dialog:
<div class="container tw-py-20"> <div class="tw-max-w-xl"> <p class="text-muted"> <span data-toggle="tooltip" title="Organization name"><%= @organization.name %></span> / <%= link_to 'Team members', dashboard_organization_team_members_path(@organization) %> </p> <%= turbo_frame_tag 'modal_dialog' do %> <h1 class="tw-m-0 tw-mb-6">Invite team member</h1> <%= render partial: 'form', locals: { team_member_form: @team_member_form, form_method: :post, action_url: dashboard_organization_team_members_path, submit_value: 'Invite' } %> <% end %> </div> </div>
Controller:
def create team_member_form = TeamMemberForm::NewForm.new(new_team_member_params.merge(organization: organization, inviter_email: current_user.email)) if team_member_form.save flash[:success] = 'The user has been added to the team members.' respond_to do |format| format.turbo_stream do render turbo_stream: turbo_stream.action(:redirect, dashboard_organization_team_members_path(organization)) end format.html do redirect_to dashboard_organization_team_members_path(organization) end end else @team_member_form = team_member_form render :new end end
Redirect:
// Workaround for redirecting from within a Turbo Frame: // https://github.com/hotwired/turbo-rails/pull/367 Turbo.StreamActions.redirect = function () { Turbo.visit(this.target); };
- endoflife.date
End-of-life (EOL) and support information is often hard to track, or very badly presented. endoflife.date documents EOL dates and support lifecycles for various products.
Example:
Need to check what's the latest version of Ruby? Or until when it's supported?
- stdout vs stderr
Use
stdout
for the output of your program: the log ingit log
, the matching files withfind
, or the list fromls
.Use
stderr
for errors AND ANYTHING ELSE: the progress messages, the warns/info, and the jokes (if you really need to).For example, given the following output (on
stdout
):I, [2024-12-10T18:59:45.134882 #7388] INFO -- : Crystalball starts to glow... W, [2024-12-10T18:59:45.138463 #7388] WARN -- : Maps are outdated! I, [2024-12-10T18:59:45.311953 #7388] INFO -- : Starting RSpec. {"version":"3.13.2","messages":["No examples found."],"seed":41594,"examples":[],"summary":{"duration":6e-05,"example_count":0,"failure_count":0,"pending_count":0,"errors_outside_of_examples_count":0},"summary_line":"0 examples, 0 failures"}
I had to remove manually the first lines to parse the JSON:
command | tail -n1 | jq
- Ruby: default gems
Ruby's stdlib is composed of 3 different elements:
- Standard libraries that are embedded in the language's source
- Default gems that can be required, are maintained by the core team, and cannot be removed
- Bundled gems that come installed with Ruby and can be removed
Full list on stdgems.org.
- Ruby gem activation
This is how you use a Ruby gem usually:
require 'foo' Foo.bar
For the
require
to work, the gem needs to be available on theLOAD_PATH
:ruby -e 'puts $LOAD_PATH'
The act of adding a gem to the
LOAD_PATH
is called "activating a gem".In a vanilla Ruby situation, you can add a directory to the
LOAD_PATH
with:ruby -I ./foo/lib -e "puts(require 'foo')" # true
More often, you deal with a bundle that contains a Gemfile:
gem 'foo'
To activate the gems from the Gemfile (taking into account the versions from Gemfile.lock):
require 'bundler/setup' require 'foo'
or (equivalent):
require 'bundler' Bundler.setup require 'foo'
Notice that the
setup
step will first clear theLOAD_PATH
and activate only the gems in the bundle.For example, Rails does it here and here.
If you've ever wondered what the hell is up with
bundle exec ...
, the answer is simple: Bundler activates the gems in the bundle with something likeRUBYOPT="-rbundler/setup" ruby ...
Of course, things are more complex than that. But this should give you a starting point.
- Hi, thanks, bye
When travelling, strive to learn how to say three words: hi, thanks, bye.
You'll be surprised of what happens when you stop being "just another tourist."
- Convert RDA to CSV with a script
Throw the following into
rda2csv.r
:#!/usr/bin/env Rscript argv <- commandArgs(TRUE) inFile <- toString(argv[1]) print(paste("Reading:", inFile)) outFile <- gsub(".rda$", ".csv", inFile) print(paste("Writing:", outFile)) inData <- get(load(inFile)) write.csv(inData, file=outFile)
Make it executable:
chmod +x rda2csv.r
And run it on an RDA file:
./rda2csv.r path/to/file.rda
- Risking millions on coin flips
Using the Binomial Distribution to tilt the odds in your favor.
I found this pearl in Naked Statistics (chapter 5):
In 1981, the Joseph Schlitz Brewing Company spent $1.7 million for what appeared to be a shockingly bold and risky marketing campaign for its flagging brand, Schlitz. At halftime of the Super Bowl, in front of 100 million people around the world, the company broadcast a live taste test pitting Schlitz Beer against a key competitor, Michelob. Bolder yet, the company did not pick random beer drinkers to evaluate the two beers; it picked 100 Michelob drinkers.
It turns out, Schlitz's marketing department was not a bunch of idiots. And here's why.
If you toss a coin twice you can get either one of the following:
- heads, heads
- tails, tails
- heads, tails
- tails, heads
Therefore, the probability of getting one tails out of two flips is 2/4 (ie, case 3. and 4.).
That is described by the Binomial Distribution where n is the number of trials (coins) and k is the number of successes (tails):
p = n! / [k! * (n - k)!] * (1/2)^n 2! / [1! * (2 - 1)!] * (1/2)^2 2 / 1 * 1/4 1/2 (same as 2/4 from above)
The statisticians at Schlitz considered that:
- commercial beers taste the same
- in a blind tasting, a Michelob drinker would say that Schlitz tastes better 50% of the times (it's a coin flip)
- getting 40% or more of Michelob drinkers to say Schlitz is better is worth $1.7 million
But what's the probability?
If you take 10 Michelob drinkers, you need at least 4 successes. In other words, you want to sum the probability of 4, 5, 6, 7, 8, 9, and 10 tails with coin flips:
p = 10! / [4! * (10 - 4)!] * (1/2)^10 + 10! / [5! * (10 - 5)!] * (1/2)^10 + ... 10! / [10! * (10 - 10)!] * (1/2)^10 = 0,82
So there's a 82% chance of succeeding.
And if you raise the number of drinkers to 100, you get to a shocking 98%!
- The "opposite" test
When "positioning" something, if the opposite of what you claim is bullshit, what you claim is bullshit.
If you've ever heard any of the following statements:
- Our company is customer-first
- Our product is fast & easy-to-use
Ask yourself:
- Would any company pick customer-last as a strategy?
- Would any product go for slow & hard-to-use?
Nope. Because that's bullshit!
Inspiration: https://longform.asmartbear.com/opposite-test/
- How big things get done
How big things get done published the following breakdown:
- 100% of the mega-projects analyzed
- 47.9% finished on budget
- 8.5% finished on bugdet and time
- 0.5% finished on budget, time, and produced the expected benefits
Later, the book analyzes what are the prerequisites to get into the 0.5% Olympus:
- Know why all the time: is this code rewrite benefitting the product?
- Think slow, act fast:
- Make mistakes in the planning phase when screw-ups are cheap
- The more time the execution phase takes, the more you expose yourself to black swans (ie, catastrophic and unexpected events)
- Mitigate risks:
- Spike code to understand if and how something can be done
- Ask people who worked on similar features what went wrong/could have been done better
- Estimate using anchors:
- Hofstadter’s Law: “It always takes longer than you expect, even when you take into account Hofstadter’s Law.”
- We always estimate with the happy case in mind and, even the most paranoid, cannot prepare for unknown unknowns
- Ask others what was their experience building a similar thing and use their numbers as a starting point
- Lean on experience:
- Involve people who've worked on similar features in the past
- Learn from other similar projects—and no, you are not working on anything so unique that cannot draw from experience
- Use boring tech: "remove the words custom and bespoke from your vocabulary"
- Build a team
- Iterate:
- "we're terrible at getting things right the first time"
- "I have not failed ten thousand times," Thomas Edison said. "I’ve successfully found ten thousand ways that will not work."
- "If something works, you keep it in the plan. If it doesn’t, you “fail fast,"
- When talking about the success of the Empire State Building, "workers didn’t build one 102-story building, they built 102 one-story buildings."
The following sentence made me stop and think about my fuck-ups:
When delivery fails, efforts to figure out why tend to focus exclusively on delivery. That’s understandable, but it’s a mistake, because the root cause of why delivery fails often lies outside delivery, in forecasting, years before delivery was even begun.
- Learning requires sweating
Learning is not supposed to be fun. It doesn't have to be actively not fun either, but the primary feeling should be that of effort.
If you are not sweating, it's entertainment, not learning.
Source: Andrej Karpathy on X
- Horizontally scroll to center an element
<div class="width-full overflow-x-auto"> <div>...</div> <div>element</div> <div>...</div> </div>
const viewportWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0) container.scrollLeft = element.offsetLeft // scroll to the left edge of element + element.offsetWidth / 2 // scroll to center of element - viewportWidth / 2 // scroll back half the viewport
- Comment your regular expressions
# ❌ usa_postal_code_pattern = /\A\d{5}(-\d{4})?\z/ # ✅ usa_postal_code_pattern = %r{ \A # Beginning of string \d{5} # 5 digits ( # ZIP+4 - # Hyphen \d{4} # 4 digits )? # ZIP+4 is optional \z # End of string }x
Source: Comment your regular expressions
- Dreamweaving unforgettable experiences
Will Guidara hires Dreamweavers in his restaurant.
Their job is to pull legends: creating, automating, and scaling unforgettable experiences.
- The 95/5 rule
Balancing out what's best for the user vs what's best for the business
Every decision I made seemed to expose the natural tensions between improving the quality of the experience the guests were having and doing what was best for the business. Restaurant-smart meant leading with trust—including allowing the people who worked for me to do what they felt was best for the guests. Corporate-smart meant running a tight ship. Which was right?
Will Guidara's answers his own question:
Manage 95% of your financials down to the penny while spending the remaining 5% “foolishly”, and “splurging” the 5% on the guest experience to create an unforgettable experience.
- Find unused indexes in Postgres
How to use the `pg_stat_user_indexes` table
SELECT * FROM pg_stat_user_indexes WHERE relname = 'TABLE_NAME'
:idx_scan
: how many times the index was usedidx_tup_read
: how many index entries were returned using the indexidx_tup_fetch
: how many rows were returned using the index
You want:
idx_scan
to move up (ie, the index is used)idx_tup_read
/idx_tup_fetch
to move up but not too much (ie, the index is highly selective)
- How to find content ideas
A cheap trick for unlimited inspiration
- Find a relevant subreddit
- Google:
"how to" site:reddit.com/r/SUBREDDIT/
Here are the top results for
"how to" site:https://www.reddit.com/r/javascript/
:- "How to actually Learn from the Documentation?"
- "How do I practice JavaScript?"
- "How did you learn Javascript?"
- "How to become a more well rounded dev"
- "How to develop android apps with javascript?"
- Html `<template>`
Hidden and re-usable pieces of Html
In some situations, you may want to render some hidden Html that can be later accessed by JavaScript to use as a template.
For example, a form to create a survey with a title and multiple questions:
<form> <input type="text" name="title" placeholder="Enter title" /> <button type="button" onclick="addQuestion()">Add question</button> <p>Question</p> <input type="text" name="question" placeholder="Enter question" /> </form> <template> <p>Question</p> <input type="text" name="question" placeholder="Enter question" /> </template> <script> function addQuestion() { const template = document.querySelectorAll("template")[0].content; const clone = template.cloneNode(true); document.querySelectorAll("form")[0].appendChild(clone); } </script>
What I used in the past is less elegant:
<form> <input type="text" name="title" placeholder="Enter title" /> <button type="button" onclick="addQuestion()">Add question</button> <p>Question</p> <input type="text" name="question" placeholder="Enter question" /> </form> <div class="template" style="display: none;"> <p>Question</p> <input type="text" name="question" placeholder="Enter question" /> </div> <script> function addQuestion() { const template = document.querySelectorAll(".template")[0]; const clone = template.cloneNode(true); clone.style.display = "block"; document.querySelectorAll("form")[0].appendChild(clone); } </script>
- Logarithm laws
A quick recap of some fundamental properties of logarithms
Power law
logbpn = n * logbp
Proof:
logbp = a
->ba = p
Raise to
k
->bk*a = pk
Apply
logb
->k * a = logbpk
Replace
a
withlogbp
->k * logbp = logbpk
Product law
logbm*n = logbm + logbn
Proof:
logbm = i
->bi = m
&logbn = j
->bj = n
Multiply ->
bi * bj = m * n
Simplify ->
bi+j = m * n
Apply
logb
->i + j = logbm*n
Replace
i
andj
->logbm + logbn = logbm*n
Quotient law
logbm/n = logbm - logbn
Proof:
logbm = i
->bi = m
&logbn = j
->bj = n
Divide ->
bi / bj = m / n
Simplify ->
bi-j = m / n
Apply
logb
->i - j = logbm/n
Replace
i
andj
->logbm - logbn = logbm/n
- Video to Gif oneliner
Using `ffmpeg` to transform an .mp4 into a .gif
This is how I created the demos for 3v0k4/exit.nvim:
- Recorded the screen with QuickTime into an .mp4
- Transformed into a .gif with
ffmpeg
ffmpeg \ -i input.mp4 \ -vf "fps=10,scale=640:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \ -loop 0 \ output.gif
Source: SuperUser