惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News | PayPal Newsroom
云风的 BLOG
云风的 BLOG
GbyAI
GbyAI
Engineering at Meta
Engineering at Meta
B
Blog RSS Feed
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
The Register - Security
The Register - Security
L
LangChain Blog
A
About on SuperTechFans
S
Schneier on Security
博客园 - 三生石上(FineUI控件)
Stack Overflow Blog
Stack Overflow Blog
The Hacker News
The Hacker News
AWS News Blog
AWS News Blog
博客园 - 司徒正美
Scott Helme
Scott Helme
K
Kaspersky official blog
Cyberwarzone
Cyberwarzone
T
Tenable Blog
腾讯CDC
Recorded Future
Recorded Future
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
G
GRAHAM CLULEY
Security Latest
Security Latest
S
Securelist
D
Darknet – Hacking Tools, Hacker News & Cyber Security
aimingoo的专栏
aimingoo的专栏
Google DeepMind News
Google DeepMind News
V
Vulnerabilities – Threatpost
雷峰网
雷峰网
T
The Exploit Database - CXSecurity.com
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V
V2EX
T
The Blog of Author Tim Ferriss
D
Docker
S
Security Affairs
F
Full Disclosure
Know Your Adversary
Know Your Adversary
N
News and Events Feed by Topic
N
News and Events Feed by Topic
T
Tor Project blog
Hugging Face - Blog
Hugging Face - Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Microsoft Security Blog
Microsoft Security Blog
Simon Willison's Weblog
Simon Willison's Weblog
Recent Announcements
Recent Announcements
博客园_首页
博客园 - 聂微东
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
Security @ Cisco Blogs

Hacker News: Front Page

SPICE simulation → oscilloscope → verification with Claude Code — Lucas Gerads GitHub - GainSec/AutoProber: Hardware hacker’s flying probe automation stack for agent-driven target discovery, microscope mapping, safety-monitored CNC motion, probe review, and controlled pin probing. Introducing Claude Opus 4.7 Qwen Studio The Future of Everything is Lies, I Guess: Where Do We Go From Here? GitHub - SeanFDZ/macmind: Single-layer transformer in HyperTalk for the classic Macintosh Show HN: Agent-cache – Multi-tier LLM/tool/session caching for Valkey and Redis Ancient DNA reveals pervasive directional selection across West Eurasia [pdf] AI cybersecurity is not proof of work Moving a large-scale metrics pipeline from StatsD to OpenTelemetry / Prometheus GitHub - Nightmare-Eclipse/RedSun: The Red Sun vulnerability repository GitHub - SethPyle376/hiraeth: Local AWS emulator focused on fast integration testing, with SQS support, SQLite-backed state, and a debug-friendly web UI. A Better Ludum Dare; Or, How to Ruin a Legacy GitHub - macOS26/Agent: Any AI, replaces Claude Code, Cursor, OpenClaw. Over 18 LLM providers (Claude, OpenAI, Gemini, Ollama, Zai, HF, Qwen) wired into a native Mac app that writes code, builds Xcode projects, bumps versions, manages git, automates Safari, use AppleScript, JS or Accessibility, extend Agent! w/ MCP Servers, run tasks from your iPhone via Messages. YouTube now lets you turn off Shorts I Made a Terminal Pager Burgers | マクドナルド公式 Commands — HackerNews CLI documentation ChatGPT for Excel PiCore - Raspberry Pi Port of Tiny Core Linux Live Nation illegally monopolized ticketing market, jury finds Google Broke Its Promise to Me. Now ICE Has My Data. Founding Engineer at Adaptional | Y Combinator CRISPR takes important step toward silencing Down syndrome’s extra chromosome GitHub - saffron-health/libretto: The AI toolkit for building reliable browser automations US v. Heppner (S.D.N.Y. 2026) no attorney-client privilege for AI chats [pdf] Unexpected €54k billing spike in 13 hours: Firebase browser key without API restrictions used for Gemini requests Fragments: April 14 Cal.com Goes Closed Source: Why AI Security Is Forcing Our Decision | Cal.com - Scheduling Software for Online Bookings Laravel raised money and now injects ads directly into your agent Pakistan hospital at centre of child HIV outbreak caught reusing syringes in BBC film Codex Hacked a Samsung TV Tech Valuations Back to Pre-AI Boom Levels A perfectable programming language — Soter GitHub - halfwhey/claudraband: Claude Code for the Power User Partnership through Play: Investigating How Long-Distance Couples Use Digital Games to Facilitate Intimacy Textbooks and Methods of Note-Taking in Early Modern Europe (2008) Eternity in six hours: Intergalactic spreading of intelligent life (2013) Seven countries now generate 100% of their electricity from renewable energy Tell HN: OpenAI silently removed Study Mode from ChatGPT Pro Max 5x Quota Exhausted in 1.5 Hours Despite Moderate Usage Show HN: Oberon System 3 runs natively on Raspberry Pi 3 (with ready SD card) Tell HN: docker pull fails in spain due to football cloudflare block Bring Back Idiomatic Design No one owes you supply-chain security GitHub - xsawyerx/curl-doom: DOOM, played over cURL Apple update turns Czech mate for locked-out iPhone user The Grand Line Cache TTL silently regressed from 1h to 5m around early March 2026, causing quota and cost inflation Building a Z-Machine in the worst possible language The peril of laziness lost Iran war: We spoke to the man making Lego-style AI videos that experts say are powerful propaganda AI Will Be Met With Violence, and Nothing Good Will Come of It GitHub - duguyue100/midnight-captain: Inspired by Midnight Commander, tailored to my taste. How to build a `git diff` driver · Jamie Tanna | Software Engineer Center for Responsible, Decentralized Intelligence at Berkeley The Local Universe’s Expansion Rate Is Clearer Than Ever, but Still Doesn’t Add Up - A new synthesis of astronomical measurements confirms a persistent mismatch that could point to physics beyond current models The disturbing white paper Red Hat is trying to erase from the internet – OSnews NetBlocks (@netblocks@mastodon.social) The Future of Everything is Lies, I Guess: Annoyances ‘Abhorrent’: the inside story of the Polymarket gamblers betting millions on war Productive procrastination — Max van IJsselmuiden maps, territory and LMs 447 Terabytes per Square Centimetre at Zero Retention Energy: Non-Volatile Memory at the Atomic Scale on Fluorographane Show HN: Pardonned.com – A searchable database of US Pardons 20 Years on AWS and Never Not My Job The Seasons are Wrong The FAA wants gamers to apply for air traffic control jobs Artemis II crew splashes down near San Diego after historic moon mission Why weekends are under threat We gave an AI a 3 year retail lease in SF and asked it to make a profit | Andon Labs How a dancer with ALS used brainwaves to perform live On filing the corners off my MacBooks Installing every* Firefox extension OpenClaw’s memory is unreliable, and you don’t know when it will break Steve Blank Nowhere Is Safe Chimpanzees in Uganda locked in vicious 'civil war', say researchers watgo - a WebAssembly Toolkit for Go linux/Documentation/process/coding-assistants.rst at master · torvalds/linux GitHub - callumlocke/json-formatter: Makes JSON easy to read. Founding Product Engineer at Bild AI | Y Combinator A compelling title that is cryptic enough to get you to take action on it GitHub - Keychron/Keychron-Keyboards-Hardware-Design: Industrial design files for Keychron keyboards and mice. 100+ models with CAD assets in STEP, DXF, DWG, and PDF. Source-available, with commercial use allowed for original compatible accessories within the license terms. [ANNOUNCE] WireGuardNT v0.11 and WireGuard for Windows v0.6 Released 1D-Chess Helium Is Hard to Replace Keeping a Postgres queue healthy — PlanetScale Serenity Forge (@serenityforge.com) Our response to the Axios developer tool compromise Do Americans read print books, e-books or audiobooks more? Uncharted island soon to appear on nautical charts The Problem That Built an Industry Fragments: April 2 Python Release Python install manager 26.1 Bitcoin miners are losing $19,000 on every BTC produced as difficulty drops 7.8% God sleeps in the minerals Harness engineering: leveraging Codex in an agent-first world Apple Silicon and Virtual Machines: Beating the 2 VM Limit What have been the greatest intellectual achievements? The APL Programming Language Source Code
You Must Fix Your Asserts
May 31, 2026 • 14 min read • by Loris Cro · 2026-05-31 · via Hacker News: Front Page

Fear is the killer of the mind ...and the codebase as well.

A user on a discussion platform wrote:

I think “disabling asserts in prod” is a pretty common technique, yeah?

As far as I know that is probably a correct statement, but I believe it to be an irredeemably bad practice. Let’s start with some context first, since this discussion started because of how std.debug.assert works in Zig.

Asserts in general

An assert is a line of code that introduces a new fact to the program, such as “this argument can never be null”, or “this integer can never be even”, and they kinda look like this:

assert(my_arg != null);
assert(my_num % 2 != 0);

If your type system can be used to enforce one of these constraints, then you will probably want to use the facilities in your language rather than asserts.

For example, in Zig normal pointers (e.g. *Foo) can never be null, while optional pointers (?*Foo) can, but they also force you to check before you can access the value (and for which Zig has dedicated idioms).

Asserts can be used to explicitly state pre/post conditions and invariants in your code. This is useful because, if you pick good assertions, those will be able to protect you from programming mistakes better than unit tests, especially if you fuzz your code.

An assert is worth a thousand unit tests (and orders of magnitude more than that if you fuzz), but that’s a story for a follow-up post.

Asserts in Zig

Asserts in Zig are based on unreachable, a language feature that marks invalid code paths.

const Op = enum { a, b, c };

fn execute(orig_op: Op) void {
  var op = orig_op;

  if (op == .a) {
    op = .b; // turn .a into .b
  }

  const op_cost = switch(op) {
    .a => unreachable, // impossible to reach
    .b => 50,
    .c => 100,
  };

  // finalize op
}

In this example the .a case is always mutated into a .b case by the if statement which means that, once we reach the switch, it’s impossible to enter the .a case.

Another neat property of unreachable is that it can be used as a statement, but it is also valid anywhere an expression (of any type) is expected.

In the example above we’re computing the “cost” of an operation, and it might be that it doesn’t even make sense for .a to have an associated cost. Thanks to unreachable we don’t even have to come up with an awkward placeholder value for a case that can never happen anyway.

Zig’s stdlib assert function also leverages unreachable and is implemented as follows:

pub fn assert(ok: bool) void {
  if (!ok) unreachable; // assertion failure
}

Build modes

Zig has multiple build modes:

  • Debug
  • ReleaseSafe
  • ReleaseFast
  • ReleaseSmall

This is not a setting that is necessarily global to your program: every dependency can be built in a different mode and you can even use @setRuntimeSafety for block-level granularity within a single function.

When an assert is tripped “illegal behavior” occurs. Checked modes (Debug, ReleaseSafe, @setRuntimeSafety(true)) guarantee a crash of your program by panicking, while unchecked modes (ReleaseFast, ReleaseSmall, @setRuntimeSafety(false)) incur “unchecked illegal behavior”.

In short, unchecked illegal behavior means that the program will misbehave.

In this particular example what happens today is that the switch statement that assigns a value to op_cost will ‘fallthrough’ to one of the other cases because of how the machine code gets generated. But that’s not guaranteed, and a different version of the compiler might generate machine code that causes a different misbehavior.

Here’s a godbolt link so you can see for yourself.

This is a sharp tool, but it’s what powers a lot of powerful optimizations, for example in our case the machine code necessary to implement the first branch of the switch statement was essentially elided from the final executable.

Here’s another godbolt link where you can see how an assert interacts with the subsequent switch statement in both ReleaseSafe and ReleaseFast (note how in ReleaseFast the function skips all comparisons and just returns true).

This is the kind of stuff that videogames and other real-time media applications rely on massively.

Not every assert will lead to a performance increase, but optimizing compilers have the ability to propagate unreachable information, resulting in non-local optimizations that you might not be able to easily anticipate as a programmer.

Zig asserts are not macros

When approaching Zig, one thing that surprises C/C++ developers especially, is the fact that std.debug.assert is not a macro (and FYI Zig doesn’t have macros).

In those languages, it is common to disable assertions in a way which essentially acts as a though every call to assert had been commented out, including whatever expression is passed to the macro.

This means that in C/C++ you should never put an expression with side effects into a call to assert, as that whole operation will be commented out when asserts are disabled.

In Zig this is just not a thing because std.debug.assert is a normal function, which means that its arguments are evaluated before calling it no matter what the logic inside the function is.

The result is that you can put expressions with side-effects in your calls to assert without fear:

// assert that the remove operation is not a noop:
assert(my_map.remove("expected-to-exist"));

On the flipside it also means that if you have an assert that relies on performing complex computations, then those won’t necessarily be elided when building in unchecked modes, in which case you need to take care to guard the code with a comptime if:

const builtin = @import("builtin");

if (builtin.mode == .Debug) {
  var condition = ...;
  // whatever bookkeeping is necessary
  // to compute the condition
  assert(condition == .ok);
}

This is surprising behavior if you’re used to C/C++ semantics, but at the same time, if you’re a seasoned developer, you should be able to wrap your head around function call semantics eventually.

This is a good opportunity to get rid of some macro-induced PTSD and embrace simplicity, especially because in Zig you don’t normally disable asserts, which brings me to the main point of this post.

Disabling asserts in prod

To recap, there are three things you can do with asserts:

  1. Keep them as runtime checks that panic the process when tripped.
  2. Use them for performance optimization at the cost of program misbehavior if an assert turns out to be wrong.
  3. Completely disable them. This is not supported out of the box by std.debug.assert, but you can implement your own version that internally checks a build-time flag, approximating C/C++ behavior 1.

As I mentioned in the beginning, I believe (3) to be an irredeemably bad choice.

What are the reasons to want to disable asserts? It’s essentially the union of the other two cases, negated:

  1. You don’t want to keep the runtime checks, either because of the performance cost or because you don’t want the application to crash.
  2. You don’t want to use asserts for optimization because you don’t trust them to be correct and thus fear program misbehavior.

As matklad reminded me in a recent discussion on the topic, there might be situations where you might have a legitimate engineering reason to want to avoid crashes, but as far as general software goes, that’s a pretty bad default choice in my opinion.

Disabling asserts means that when one of those presumed-impossible conditions actually happens, the program keeps running instead of crashing. So now you have a program that keeps running under wrong assumptions, which is still a form of misbehavior, even if not caused by unchecked illegal behavior (UIB) as described above.

Naive memory safety advocates might argue that UIB (or undefined behavior, as it’s called in C) is infinitely worse, but I would disagree.

What makes UIB dangerous is the fact that it’s a pathway for turning a program into a weird machine, but in software sufficiently complex you don’t necessarily need UIB to twist the program into one. Falsifying an assertion at runtime is by definition deviating from the spec, and it can easily be enough to make the program perform operations that it was never intended to do.

And that’s not just a technicality: SQL injection is a concrete and widespread example of weird-machine-grade misbehavior that doesn’t require UIB.

If the cost of program misbehavior is so high that you don’t want to risk it, then you should keep the asserts on, and if performance is so important that you’re willing to risk misbehavior, then you’re just leaving performance on the table, while thinking that you’re safer than you really are.

But there’s an even bigger reason why methodically disabling all asserts in prod is counterproductive.

Gaslighting yourself

To recap, the crux of the issue here is about the possibility of asserts being wrong, and the consequences of that. If we could guarantee that all our asserts are always true, then always using them for performance optimization would be uncontroversial. Similarly, if we could guarantee that tests could catch all wrong asserts, then prod could always be optimized safely as well.

The reason you’re reading this post, is because we know that we could write a wrong assert and it’s not guaranteed that tests would catch it, which is not just a hypothetical. There are plenty of projects that have asserts that pass tests, but that trip in prod, I can think of a hyped one that recently left the Zig ecosystem, for example.

If this is the situation that you’re in, then it is in your best interest to discover all wrong asserts in your code as soon as you can, because otherwise you will keep writing more code that mistakenly relies on those wrong assertions, worsening the problem.

Imagine a big codebase with this somewhere in it:

fn processThing(thing: Thing) void {
   // this function must always be invoked on
   // a thing that has already been started
   assert(thing.is_started);

   // ...
}

Some time passes, the assert seems to hold because it never trips in testing, and you never discover that this assert is actually falsifiable, because you disable asserts in prod. But maybe it turns out that it’s not that bad, no user reports misbehavior in prod, and life goes on.

After a while somebody adds more code below:

fn processThing(thing: Thing) void {
   // this function must always be invoked on
   // a thing that has already been started
   assert(thing.is_started);

   // ...

   // Since thing is already started, we don't
   // need to foo the bar before bazzing the qux.
   // It would be really bad to baz the qux otherwise,
   // so we add an assert for good measure.
   assert(thing.is_fooed);
   thing.baz(qux);
}

Assuming this second developer wrote a correct assert (i.e. thing being started implies thing being fooed by the time processThing is called), since in testing the first assert never trips, the second also never trips, but ironically this might be the moment where an exploitable vulnerability is introduced to the codebase and you don’t realize it, because you disabled asserts in prod.

It’s already hard to write correct code in the first place, but it’s unreasonably hard to do so when the code has asserts that effectively gaslight you.

In conclusion

Depending on the context, different programs will have different priorities, and for some it’s the legitimately correct choice to prefer performance over minimizing risk of misbehavior, in which case turning asserts into optimization opportunities makes perfect sense.

Routinely disabling asserts in prod is suboptimal to both keeping asserts on and optimizing for performance, and I find it absurd that people would engage in this practice uncritically, while at the same time being extremely critical of ReleaseFast.

I have two serious projects that I’m working on.

The first one is Zine, a static site generator. I haven’t defined a threat model for it, and doing so is not my top priority since today it’s mainly used to build personal blogs. I also like seeing it run an order of magnitude faster than Hugo, so I publish ReleaseFast builds.

The second one is Awebo, a pre-alpha stage, self-hostable Discord alternative. In this case I already know that it’s software that will deal with personal information and that is meant to be exposed to the internet. When the time comes, I’ll publish ReleaseSafe builds, but even then I will still build some critical dependencies in ReleaseFast (FFmpeg, Xiph Opus, SQLite, etc) because in their case the performance increase is strictly preferable over further minimizing risk of program misbehavior.

To name two more interesting examples, TigerBeetle (financial database) always keeps asserts on, while Ghostty (terminal emulator) distributes ReleaseFast builds for macOS and recommends that downstream consumers (e.g. Linux distro maintainers) do the same.

Also interestingly, the only two relatively serious Ghostty CVEs published so far are about arbitrary command execution achieved without memory corruption, despite Ghostty being distributed in ReleaseFast.

When it comes to the genral practice of “disabling asserts in prod”, I bet that there are a lot of projects out there where wrong asserts fester and multiply in the codebase, which in turn both increases UIB paranoia and makes developers subconciously too scared to turn asserts back on and face the consequences of their actions.

The reality is that there’s no way around it: you must fix your damn asserts and strive for program correctness, not just for a subset of it.


EDIT(2026-06-01): after discussing this blog post a bit, it was pointed out to me that I could have given more space to other variants of keeping asserts on in prod. For simplicity I’m just pasting here a quote from a message I wrote as part of that discussion. You will be missing a bit of the context, but the important stuff is all there.

You add assertions for a variety of reasons but regardless they have an inherent axiomatic nature for the compiler (ie if they’re not trivially redundant, they are facts that the compiler cannot derive on its own), and so that fact can be exploited in a variety of ways:

  • better debuggability,
  • extra checks at runtime to lower the chance of program misbehavior,
  • extra facts that the compiler can use for optimization,

For the second case, Zig chooses crashing as the default mechanism, but if you really want you can fuck around with the panic handlers and implement something similar to a recoverable panic like in rust/go (a simpler version, and maybe that’s for the better, the go one in particular has gnarly footguns), but Zig aims at ‘perfect software’ so it’s not what you get out of the box.

I have never in my life written an assertion with the explicit intent of obtaining a performance increase, and if I ever will, it will be a different process that involves measuring. My expectation is that, when building in ReleaseFast, the compiler will try best-effort to make use of the extra axioms I’m providing, as it already does with language-level asserts (oob, overflow, etc).

And the thing is that you can get all of these things out of asserts at the same time by switching build mode (and curating your asserts a little when necessary, like reserving expensive asserts for debug mode).

Another variant of the second case is to have the assert print a log line instead of crashing the program, which makes sense when crashing is worse than continuing, panic recovery is complex and more prone to errors, and a human will have access to those logs and is eventually going to get a chance to fix falsifiable asserts. This is another thing that Zig doesn’t give you out of the box, but this one is especially trivial to implement.

Given all of this, turning off asserts in release builds to me seems the dumbest thing one could do, second only to deleting asserts from the codebase or straight up never writing them.

Turning asserts into log messages sounds a lot like disabling them, but it’s not the same thing, since full on disabling means that nothing will ever be able to notify you that the program deviated from the spec.