28 comments

[ 3.2 ms ] story [ 37.8 ms ] thread
That's a nice compendium of tips and useful information.

I wonder if anyone can learn from this. I feel like I only understood what I already knew, or at least was very close to knowing. That's the same thing that happens with teaching manuals about any topic: they're organized in a way that makes sense and it's easy for people who already know the topics, but often very bad at teaching the same topics to an audience that doesn't know anything.

A recent trap for me:

Regex semantics is subtly different across languages. E.g. a{,3} matches between 0 and 3 "a" characters in Python. In JavaScript it matches the literal string "a{,3}".

CSS and C++ both have the “pick a subset and enforce that, or suffer” nature. On my to-do list: make a github action that requires manual override to merge any pull request with a css attribute not already present
(comment deleted)
> Java, C# and JS use UTF-16-like encoding for in-memory string

That’s incorrect for Java, possibly also for C# and JS.

In any language where strings are opaque enough types [1], the in-memory representation is an implementation detail. Java has been such a language since release 9 (https://openjdk.org/jeps/254)

[1] The ‘enough’ is because some languages have fully opaque types, but specify efficiency of some operations and through it, effectively proscribe implementation details. Having a foreign function interface also often means implementation details cannot be changed because doing that would break backwards compatibility.

> JS use floating point for all numbers. The max accurate integer is 2⁵³−1

That is incorrect. Much larger integers can be represented exactly, for example 2¹⁰⁰.

What is true is that 2⁵³−1 is the largest integer n such that n-1, n, and n+1 can be represented exactly in an IEEE double. That, in turn, means n == n-1 and n == n+1 both will evaluate to false, as expected in ‘normal’ arithmetic.

This looks like not so much traps, but a list of things the author has learned.

Much of it would only apply in certain relatively narrow contexts, but the contexts aren't necessarily mentioned.

Some of it appears to be just wrong.

I guess I'm saying: I would not take this literally, but as something almost like a stream-of-consciousness.

Does anyone truly understand all the little edge cases with CSS?

I've write tons and tons of CSS, have done for a decade. I don't sit and think about the exact interactions, I just know a couple things that might work if I'm getting something unexpected.

I don't really see it possible to commit that to memory, unless I literally start working on an interpreter myself.

> A method that returns Optional<T> may return null.

projects that do this drive me bananas

If I had the emotional energy, I'd open a JEP for a new @java.lang.NonNullReference and any type annotated with it would be a compiler error to assign null to it

  public interface Alpha {}
  @java.lang.NonNullReference
  public interface Beta {}

  Alpha a = null; // ok
  Beta b = null; // compiler error
javac will tolerate this

  Beta b;
  if (Random.randBoolean()) {
    b = getBeta();
  } else {
    b = newBeta();
  }
but I would need to squint at the language specification to see if dead code elimination is a nicety or a formality

  Beta b;
  if (true) {
    b = getBeta();
  } else {
    b = null; // I believe this will be elided and thus technically legal
  }
> Unset variables. If DIR is unset, rm -rf $DIR/ becomes rm -rf /. Using set -u can make bash error when encountering unset variable.

sweet mercy :O

Someone call the Inquisition

> Some routers and firewall silently kill idle TCP connections without telling application. Some code (like HTTP client libraries, database clients) keep a pool of TCP connections for reuse, which can be silently invalidated. To solve it you can configure system TCP keepalive. For HTTP you can use Connection: keep-alive Keep-Alive: timeout=30, max=1000 header.

Once a TCP connection has been established there is no state on routers in between the 2 ends of the connection. The issue here is firewalls / NAT entries timing out. And indeed, no RSTs are sent.

We had the issue in K8s with the conntrack module set too low.

Now, you can try to put in an HTTP Keep-Alive, but that will not help you. The HTTP Keep-Alive is merely for connection re-use at the HTTP level, i.e. it doesn't close the connection: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/...

An HTTP Keep-Alive does not generate any packages, it merely postpones the close.

A TCP Keep-Alive generates packages which resets the timers.

Note that TCP Keep-Alive might not play well with mobile devices.

Mobile operating systems can use application-level keep-alive packets, because those can be easily attributed to individual applications: an applications receives a TCP/UDP packet during low-power CPU sleep mode, asks system to wake up (by e.g. taking a wake-lock), and the system takes note who caused the wake-up. TCP Keep-Alive happens below application level, so it may be disabled, even when application can still be reached.

Largely a good listicle. Some feedback:

> Unicode unification. Different characters in different language use the same code point. Different languages' font variants render the same code point differently. 語

This isn't a trap. The given example character means the same thing in Chinese and Japanese, and the Japanese version was imported from China. People from both languages recognize both font variants as the same conceptual character.

The author is making it sound like the letter 'A' in English should have a different code point than an 'A' in French. Or that a lowercase 'a' with the top tail should be a different character than a lowercase 'a' without the top tail.

Anyway, this is discussed at length in https://en.wikipedia.org/wiki/Han_unification

> There is a negative zero -0.0 which is different to normal zero. The negative zero equals zero when using floating point comparision. Normal zero is treated as "positive zero".

And there are two ways to distinguish negative zero from normal zero: By their integer bit patterns, or by the fact that 1.0/-0.0 == -Inf vs. 1.0/0.0 == +Inf.

> It's recommended to configure the server's time zone as UTC.

Big yes. I use UTC for servers, logs, photos, and anything that is worth archiving and timestamping properly. Local time is only for colloquial use.

> For integer (low + high) / 2 may overflow. A safer way is low + (high - low) / 2

Yes, but if low and high could be negative numbers, then you've just shifted the overflow to a different range. This matters for general binary search over an integer range, as opposed to unsigned binary search over an array.

> C/C++

I'm going to throw in one of my lists of pitfalls - just using integer types and arithmetic correctly in C/C++ is a massive developer trap. That's like the most basic thing in programming. https://www.nayuki.io/page/summary-of-c-cpp-integer-rules

> Rebase can rewrite history

"Can" is a weasel word; rebase does nothing but rewrite history.

> People from both languages recognize both font variants as the same conceptual character.

A character that carries the same concept yes. A mere "font variant" no. It's absolutely a trap to think that you can safely replace one character with another just because they have the same unicode codepoint; Japanese people will avoid your product if you do this.

> There are subtle differences between numpy and pytorch.

This isn't really a trap, and it doesn't help anyone; it looks like "I got burned but I don't want to share the specifics".

> Division is much slower than multiplication (unless using approximation). Dividing many numbers with one number can be optimized by firstly computing reciprocal then multiply by reciprocal.

Is this a general fact or specific to a language?

> Python: - Default argument is a stored value that will not be re-created on every call.

PSA for anyone working with datetime variables!

I'm not regularly a python developer and I just spent a ton of time earlier this week tripped up by the default argument being a stored value. I was using to make an empty set if no set was passed in... but the set was not always empty because it was being reused. Took me forever to figure out what was going on.
> If you already use locking, no volatile needed.

Kinda misleading. volatile is for memory mapped I/O and such. volatile means the memory access really happens

"Associativity law and distribution law doesn't strictly hold because of inaccuracy." should be due to precision loss not inaccuracy (they are different).
The biggest trap of all: building things that no one, including yourself, wants
Adjacent to the bit about 100vh, I’d add an item about the much older fundamental stupidity of viewport units, which Firefox tried to fix but no one else got on board and so they eventually removed their patch and went back to being as broken as everyone else. That is: they ignore scrollbars. Use a platform with 17px-wide scrollbars, and in a document with vertical scrolling, 100vw is now 100% of the viewport width… plus 17px. People get this wrong all the time, and it’s only increased as more platforms have switched to overlay scrollbars by default.

> Blur does not consider ambient things.

Need to make that backdrop blur.

> Whitespace collapse. HTML Whitespace is Broken

I always hated that article title. It’s not broken, it’s just different from what you occasionally expected, mostly for very good reasons that let it do what you expected most of the time. I also disagreed with some significant other parts of it: https://news.ycombinator.com/item?id=42971415.

> Two concepts: code point (rune), grapheme cluster:

I have problems with this lot.

Firstly: add code units. When dealing with UTF-8 and you can access individual bytes, they’re UTF-8 code units. When dealing with UTF-16 and you can access 16-bit values, including surrogates, they’re UTF-16 code units.

Secondly: don’t talk about code points in general, talk about scalar values, which are what code points would have been were it not for that accursèd abomination UTF-16. Specifically, scalar values excludes the surrogate code points.

Sensible things work with scalar values. Python is the only thing I can think of that gets this wrong: its strings are sequences of code points, so you can represent lone surrogates (representable in neither UTF-8 nor UTF-16). This is so stupid.

Thirdly: ditch the alias “rune”, Go invented it and it never caught on. I don’t know of anything else that uses the term (though maybe a few things do), and the term rune gets used for other unrelated things (e.g. in Svelte 5).

> Some text files have byte order mark (BOM) at the beginning. For example, EF BB BF is a BOM that denotes the file is in UTF-8 encoding. It's mainly used in Windows. Some non-Windows software does not handle BOM.

BOM was about endianness, and U+FEFF. Link to https://en.wikipedia.org/wiki/Byte_order_mark. It made sense in UTF-16 and UTF-32: for UTF-16, if the file starts with FE FF it’s UTF-16BE, if FF FE it’s UTF-16LE. UTF-8 application of it was always a mess, never really caught on, and I think can fairly be considered completely obsolete now: the typical person or software will never encounter it. Very little software will actually handle it any more. Fortunately, it doesn’t tend to matter much: U+FEFF is the BOM for a reason, it’s ZERO WIDTH NO-BREAK SPACE.

> YAML:

I’d add “disallows Tab indentation”. It’s the only thing I can immediately think of that’s like that. (Make is roughly the opposite, recipe lines having to start with a single Tab. Python doesn’t let you mix spaces and tabs. These are all I can immediately think of.)

For what it's worth, I really enjoyed "Traceroute Isn't Real." I have noticed for quite a while that the data from it is at best patchy, often apparently meaningless. So it's helpful to see the explanation of why that is expected behavior:

https://gekk.info/articles/traceroute.htm

(If it's outdated I'm curious if anyone knows relevant updates?)