Top
Best
New

Posted by signa11 18 hours ago

Small programming tricks(will-keleher.com)
517 points | 230 commentspage 2
disinterred 16 hours ago|
The most useful shell trick I've used to date is mapping the 'cd' command to 'zoxide', which is a more powerful version of cd that remembers folders you've been to.
BeetleB 15 hours ago||
I go a step further. When I press Alt + j, it sends all the directories in zoxide to fzf (in order of frequency), and I quickly choose the directory and jump to it.

I rarely type "cd" anymore. A huge quality of life improvement!

maleldil 11 hours ago||
You don't need to use fzf. Zoxide has an interactive function too.
qxxx 16 hours ago|||
yes! zoxide is also probably my most used tool. I did not map it to cd yet, but I use the z command to jump into directories indexed by zoxide.
1718627440 13 hours ago||
Is it much more powerful, than pushd?
SAI_Peregrinus 13 hours ago||
Yes, because you don't have to pop in order. "zoxide <subpath>" is "search the history of working directories for a path ending in <subpath> and cd to that".
cachvico 17 hours ago||
I find myself using ctrl-r less and less as I make sure that anything of value that I work out goes into a Makefile or the app tooling, for me this is the basis of the dev-ops approach to work (make sure everything is scripted, not worked out on the fly).
rootlocus 16 hours ago||
Ctrl + R is great, you don't have to type out --dangerously-skip-permissions
CableNinja 17 hours ago|||
Ctrl+r is great, sometimes. History saving sucks which can make using it difficult, but i usually use it out of laziness than trying to recall a command, especially with ssh or repeated iterations requiring flipping between two settings/states. Its hard to rely on it for other things, but its still super useful.
throwaway219450 16 hours ago||
I use history | grep quite a lot, because of the non-fuzziness with default ctrl+r. If I know exactly what the substring is, then that's great, but sometimes I also want to see the sequence around the command too (e.g. two step processes like eval ssh agent + ssh add key)
reddit_clone 16 hours ago|||
I do that too. I am trying to use Mise. (Getting interested in Maak. Haven't done anything with it yet. My Scheme is pretty rusty)

Still, Zsh history with fzf fuzzy searching is fantastic. I have years of history I can recall commands with a few keystrokes.

Truly a second-brain.

BeetleB 15 hours ago|||
Yes, but how do I know what should go in the justfile? When I've realized I've used Ctrl-r for the same thing a few times :-)

And as another commenter said, you often forget the target, and use Ctrl-r to find it. Although just does support fzf...

FlyingSnake 17 hours ago|||
I’m lazy so With fish, I just type a part of the command and press up few times to get to where I want. There’s also fish-fzf.
jamiejquinn 13 hours ago||
I've relied on this so much that I copy my entire history between machines...
RSHEPP 10 hours ago||
Self hosted Atuin would do this for you too. Unless you don't want the added tooling/complexity
jamiejquinn 3 hours ago||
Ahh I only do this when reinstalling or moving to a new laptop but it's good to know about Atuin for the future!
samdixon 17 hours ago||
ctrl+r still great for targeting your make targets :)
overflowy 17 hours ago||
If you want to level up your Zsh history experience, I built zhist[1] specifically for that. It uses fzf underneath, but provides considerably more context than simply using fzf. There's a demo video in the README.

[1] https://github.com/overflowy/zhist

AJRF 17 hours ago||
I started writing little "that's useful to remember" tips on my website. Bit more durable than shell history, and you can share them!

https://adamfallon.com/til.html

jawns 17 hours ago||
I would love for someone to package up some kind of script or AI skill that evaluates your current terminal config/set-up and applies all of these tips and tricks. For instance, it might detect that you currently use ag, install ripgrep, and offer a short tutorial on how users accustomed to ag should use it. Or it might look at the history of git commands you've run and offer tips on efficiency improvements.
alentred 3 hours ago||
A lot of this kind of choices are rather opinionated and personal preferences. But anyway, I think the closets thing I can think of is oh-my-zsh.
kccqzy 17 hours ago|||
Going from ag to ripgrep is not unequivocally better. Sure ripgrep is faster, but its file filtering using -g is so much more cumbersome than -G in ag. The latter uses a regex and the former uses glob. I type many more characters to do the same search, and the same search is maybe 0.1s faster for a medium codebase. So no for me. Maybe it’s worth it for a larger codebase that isn’t large enough to require indexing.
rustyminnow 15 hours ago||
ag is also a little more ergonomic with its default flex casing: your query is case-sensitive if it has any uppercase letters, otherwise case-INsensitive. rg defaults to case-sensitive (but does have flags for insensitive and flex casing)
farrellm23 17 hours ago|||
I doubt you need a full script or skill for that. I would try something like this straight in Claude Code (and will as soon as my 5 hour quota resets :-): Review this page: https://will-keleher.com/posts/small-programming-tricks-matt... Then, review my shell init scripts (both bash and fish) and shell history. Make recommendations based on the tips and tricks from the page.
popzxc 17 hours ago||
I think it's a great way to stop understanding what's going on in your terminal.

IMHO the value of these nuggets is that you understand what you're doing and why; opening yourself to large amounts of non-default behavior likely will end up in a less than pleasant setup.

Not taking into account that different users might disagree on what is convenient and what is not, which is basically the point of making things configurable.

rf15 16 hours ago||
> You can SELECT without a FROM

welllll that depends entirely on the DB Software you're using. A certain IBM product certainly has opinions on this.

lsofzz 12 hours ago||
A small side note I want to raise:

Folks need to be sufficiently motivated to learn it. All hell break loose when `RTFM` becomes taken offensively; What do I know - I am just a weird guy with a beard.

And folks always wanted to type the commands without RTFM'ing - now we have two problems instead of one.

Also, these were hard learned tricks YOLO'ing until 3.30AM debugging a failed DNS clusters or why the HSM suddenly decided to trip up at 5AM! Nobody even cares about the things we learn these days - Google and then your AI agent already does 98% of what I know already. So, let them blow up the clusters and the racks - your pal AI agent knows. Go ask them instead.

noduerme 6 hours ago||
Not exactly programming tricks, but there's also stuff you just recognize quickly because it's a pattern you've encountered before. Like yesterday I had to diagnose a bug where some newly deployed and barely tested software hung while generating a gift certificate. Customer's name was O'Brien. Anyone wanna guess the bug?
the-mitr 7 hours ago||
One of the things I frequently use with Emacs is writing/recording macros on the fly with F3 and running them. I am sure this is present in the other editors as well, but this has helped me a lot at times

https://www.gnu.org/software/emacs/manual/html_node/emacs/Ba...

hiAndrewQuinn 1 hour ago|
I do this all the time with Vim. To the point that it astonishes me that this isn't like the third or fourth thing people learn about Vim.

You can just record and play back your exact keystrokes like a _Super Smash Melee_ game plays back your inputs to replay matches. If you can figure out how to do any mildly complex action once you can literally replay it a thousand times. Just like AutoHotKey! To my that's the essence of citizen programming!

winternewt 17 hours ago|
Only a few of these are actual programming tricks. The problem with sharing them is that they'll typically seem obvious to you, since you know them. It's difficult to know what is actually unknown to other people, and if you share stuff everybody knows you risk coming off as arrogant.

Here's one that I think more people should know: avoid branches. If I can do the same thing without an if statement and even a logical expression, the code typically both becomes easier to understand for people and easier to run for the CPU.

happytoexplain 17 hours ago||
>if you share stuff everybody knows you risk coming off as arrogant

I have always felt like my bar for publishing something (even just to internal wikis/channels) is too high due to being overly self-conscious. I think we should try not to validate that feeling by implying that there is a non-negligible number of readers who will think you have a personality flaw because you wrote down your personal collection of tips in a public place, or that those people deserve consideration in the first place.

There is no such thing as "the things everybody knows". There are just too many things. Even a list of basic tips is probably going to contain one thing I didn't know or perhaps forgot. Write-ups like this are where most of my practical knowledge comes from, not RTFM (which I do).

cachvico 17 hours ago|||
I'm struggling to comprehend how branches can be avoided (or why one would want to, as they are the cornerstone of programming). I can only think how to obfuscate them, which is rarely useful.
cestith 17 hours ago|||
Flow control is not always necessary. Other times it can be minimized. The point is not to never branch, but to avoid unnecessary ones.

It's not applicable to every situation, but one way to do this is some very basic fuzzy logic. You do a little math and then either choose a single branch at the end, or sometimes avoid a branch altogether. https://www.geeksforgeeks.org/artificial-intelligence/fuzzy-...

Another way to avoid some branches is to have specialized routines, maybe with multiple dispatch, rather than more general methods with a bunch of checks within them for slightly different situations.

A classic performance hack for critical sections is loop unrolling.

craftkiller 17 hours ago||||
Here's an example of removing a branch that was posted to HN a little over a month ago: https://www.greyblake.com/blog/branchless-rust/
metabagel 16 hours ago|||
From the article:

=====

Should you go branchless?

Most of the time, no. Branchless code is harder to read and easier to get wrong. Besides, compilers know a lot of tricks and already do a lot of this work for us.

Only when a profiler points at a hot loop, and the loop contains a branch on unpredictable data this technique can pay off big.

metabagel 17 hours ago||||
OK, but the code with the branch is easier to understand.
robby_w_g 17 hours ago|||
Yeah, I don't buy the premise that branchless code is intrinsically easier to understand. Maybe OP's point is that adding unnecessary branches makes code harder to read? But that's generally the case for any unnecessary code.
winternewt 15 hours ago||
I see stuff along the lines of:

  if (x == 0) {
      return y;
  }
  
  y += 25*x;
  return y;
and skipping the if just makes the function shorter and simpler, while also not involving the CPU branch prediction. Another one that doesn't necessarily skip all branching but at least drops one - and more importantly makes the code simpler and easy to verify, is removing the if statement in code like

  if (count == 0) {
      return;
  }

  for (int i = 0; i != count; i++) {
    puts("hello");
  }
GuB-42 11 hours ago||
Both of your examples are optimized by the compiler (gcc 16.1 -O3).

In the first case, the compiler removes the first if/return

In the second case, if you don't have the first if/return the compiler will add it. That's because it will actually convert your loop into a do/while, with the test in the end, because it is more efficient. But it has to handle the count == 0 special case first, so it will do that early return even if it is not explicitly there.

That's the kind of optimization modern compilers are good at.

jamiejquinn 17 hours ago|||
Generally agree. As with many optimisations, branchless code can easily be less obvious than the branchy equivalent.
cestith 17 hours ago|||
That's a really nice example. Thanks.
corps_and_code 17 hours ago||||
Maybe they mean rather than:

if (thingThatIsTrue):

  // a bunch of logic here...
else:

  // different logic here...

they mean:

if (thingThatIsTrue):

  return doThisWhenTrue()
return dothisWhenFalse()

Just a simple example. I'm not sure if this is what you consider "obfuscating" the branches. Logically the same, but a bit more linear to understand?

Edit: I am bad at formatting comments here.

taink 15 hours ago||
Putting two spaces before the line formats is as code.

Example:

No space before start of line.

One space before start of line.

  Two spaces before start of line.
Thus, you can put multiple lines of code with indentation as well as long as you put two spaces at the start of the line:

int main() { return 0; }

  int main() {
    return 0;
  }
See https://news.ycombinator.com/formatdoc
corps_and_code 15 hours ago||
Oh perfect, thanks!
NegativeLatency 16 hours ago||||
There's stuff like the "Command Pattern"/dispatching/subclasses etc that can make this nice, although it's not always a good fit.

Like imagine you have a few different classes of things A,B,C so instead of checking if the thing you're handling is an A,B,C you have like a shared interface across all and can call Thing.do_it or whatever.

Still branching conditionally but it's passing it off to language features instead of code you have to write.

bryanrasmussen 17 hours ago||||
well here is a branching strategy I often see, pseudocode, and often this is a really stupid example as I do not have the time to come up with a good one:

if Val === "A" then Do funcA() else if Val === "B" then

and so forth for lots of values, or using a switch statement or similar branching instead of

Object functions = { "A": funcA() {does what funcA does}, "B": funcB() {does what funcB does} etc. etc.

}

runnableFunction = functions[val]; runnableFunction();

Actually writing it I remember now someone who did this, a junior who had to update a validation function for XML invoices based on their root namespaces, which there could be a large number of these, and so she wrote out

switch namespace == "somenamespace" { validatingscheme = "someschema"; doPreliminaryFunctionToDetermineifshouldvalidate(); }

I can't remember all the details as this was almost 20 years ago, however while it was true that one branched on the schema, it made much more sense to look up what one was supposed to do based on the rule for branching and then just execute that one action rather than writing a bunch of branching logic.

So to make it more concrete: Once branching rules becomes sufficiently complex prefer query for what you should do rather than branching

on edit: note again, not real code, but should be understandable and translatable into real code to understand what is being said easily enough.

on 2nd edit: this is also just basically one of the things I prefer instead of getting a lot of branching logic. I have never seen any stats on any benefit to this model than just having a bunch of branching statements, but I feel that the benefit is there nonetheless.

BeetleB 15 hours ago||||
Probably by using various convenience functions.

A common pattern in an old C++ job I had: People writing for loops, coupled with if conditionals, for things that could just be done by chaining functions in the algorithm library.

Don't do a for loop, check for a condition, and break. Use find_if.

hermitdev 16 hours ago||||
What people refer to when they say "branchless code" is something very particular, and it refers to not triggering the CPU's branch prediction. That is, don't make the CPU have to guess which fork in the code you're going to take. This is usually accomplished in one of two ways: either bit twiddling hacks or specialized instructions that do not affect the CPU's branch prediction, such as the 'cmov' family in x86. If you search for `examples of branchless code using conditional moves` using your search engine of choice, you'll find numerous examples.

A trivial example is actually written with a branch in C/C++, but relies on compiler optimizations to kick in. If you compile a ternary operator in C/C++ (and probably rust, C# and other languages) such as in:

   int min_branchless(int a, int b) {
        return a < b ? a : b; // Often emits cmov with -O2
   }
With gcc/clang a -O2, one would expect the compiler to emit the following assembly:

    cmp edi, esi
    cmovle eax, edi   ; select a if a <= b
    ret
There's numerical tricks for other operations/comparisons, and compilers know a lot of them. But, I just suggest compiling your code and configuring your compiler to emit the generated assembly with references to the code it was generated from (you should be able to get it to emit source line references in the assembly). You'll likely be surprised at the optimizations applied at -02, and utterly confused by what you find at -03.

edit: Also, it doesn't mean to never branch, but to minimize branching, especially in tight loops. Branch outside loops, not inside, for instance.

e.g. don't do:

    for (...) {
        if (condition independent of loop variable) { 
          ...
        } else {
          ...
        }
    }
do:

    if (condition independent of loop variable) { 
        for (...) {
          ...
        }
    } else {
        for (...) {
          ...
        }
    }
a_e_k 3 hours ago|||
Yes, and back when hand-writing vectorized kernels via intrinsics, one learned to do the equivalent of (pseudocode here - picture SSE, AltiVec, NEON, etc.):

    vector conditionmask = <some computation...>; // E.g., 11111111 00000000 00000000 11111111
    vector truebranch = <some computation...>;
    vector falsebranch = <some computation...>;
    vector result = (truebranch & conditionmask) | (falsebranch & ~conditionmask);
where each lane of the conditionmask has either all bits set or all bits clear, depending on the outcome of the conditional test for that lane.

The processor obviously does execute both branches here, so there's going to be wasted work. But since it's just a linear sequence of operations it can often schedule them independently and run them out-of-order and in parallel. And of course, if there's any shared computation between the two branches, the compiler can do common subexpression elimination.

That said, that sort of approach where you go ahead and do both and then blend them was definitely the kind of optimization where you'd want to profile rather than doing it blindly. But it was a pretty common thing to do when hand-vectorizing code. (Thankfully, auto-vectorizers are pretty good at doing this sort of optimization for you these days. It's been a very long time now since I've had to hand-write vector intrinsics.)

tialaramex 9 hours ago||||
Rust is an expression language and so it doesn't have "the ternary operator"† you can use conditionals like if anywhere in your expression anyway.

If you want to tell the Rust compiler that you're certain a branch predictor can't help here [be very sure, most often humans are wrong which is why historically these "I know better than the branch predictor" features get ignored by optimisers] you can core::hint::select_unpredictable(condition, a, b) rather than using a dedicated operator.

† That's not its actual name, some languages have an operator with three operands which does something else, such as fused multiply-add so in a multi-lingual context better to say explicitly you mean the ternary conditional operator.

1718627440 13 hours ago|||
The latter example, sounds like something trivially done by the compiler. I mean I would sometimes, adhere to it, but only if the loops afterwards become substantially different. If I would just repeat most of the loop body, I would prefer the former.
owebmaster 16 hours ago|||
There are multiple ways to avoid branches. An early return, a lookup table are two that I use regularly and consider a code smell when the AI uses many if clauses or switches.
cestith 17 hours ago|||
A (hopefully interesting) aside about avoiding branches is if you don't need an exact answer but need your code to make a decision based on an approximation over some known range, you can employ a basic fuzzy logic method. Serially add, subtract, or multiply to adjust a value by a handful of weighted inputs then use that value instead of branching repeatedly to choose the right action. You might branch once based on the final value where it would have otherwise been a larger tree of decisions. In fortuitous situations, you may avoid branching altogether.
metabagel 17 hours ago|||
In what context can you avoid branches?
lscharen 17 hours ago|||
Maybe something (contrived) like this providing no-op defaults?

  total = calculateOrderTotal(user.order);
  if (user.isPremiumMember) {
    total = total * 0.9;        // 10% discount
versus

  total = calculateOrderTotal(user.order);
  discount = calculateDiscount(user);  // Returns 0.9 or 1.0
  total = total * discount;
metabagel 16 hours ago|||
OK, or maybe...

  total = calculateOrderTotal(user.order);
  total = total * user.discount;
1718627440 13 hours ago||
Or:

  total = calculateOrderTotal(user.order);
  total *= user.discount;
or:

  return 
         calculateOrderTotal(user.order)
       * user.discount;
Narishma 16 hours ago|||
Didn't you just shift the branch to the calculateDiscount() function?
1718627440 13 hours ago||
Not necessarily.

   return 1 - user.isPremiumMember * 0.1;
would also cut it.
thornewolf 14 hours ago|||
some initial function like

  v = setup()
  if v == 1:
    side_effect_1()
  elif v > 1:
    side_effect_1()
    side_effect_2(v)
  else:
    raise Exception()
then we can "refactor"

  v = setup()
  if v < 1:
    raise Exception()
  
  side_effect_1()
  if v > 1:
    side_effect_2(v)
i know that this might seem "dumb" that the code was ever setup the first way but code can grow into that shape pretty easily. this refactor "removes" the v==1 branch. this new code also follows the "early return" pattern, which improves readability.
reaperducer 17 hours ago||
if you share stuff everybody knows you risk coming off as arrogant.

Or stupid, like all those vloggers posting "ZOMG! Go all in with these secret hidden weird trick iPhone life hacks to level up!" that are just regurgitating what's in the manual.

As we used to say, RTFM: https://support.apple.com/en-us/docs/iphone

More comments...