I tried a lot of tools. Claude code, deepseek with kilocode and OMP, codex... I still use claude quite a bit. But frankly, all of them produce some absolutely godawful code. Review load went way up with AI, and it's not just the volume that caused it, but also the quality. It's extremely verbose, hard to read, often repeats code instead of factoring it into reusable components. And yes, sometimes it's also buggy. Except now, you have to debug a problem that's in code you didn't write yourself, and is awful to read.
LLM is incredibly valuable for debugging complex problems, codebase exploration, and planning large changes. But the writing code part itself, I find, LLMs are just not very good at it yet.
Vaguely telling the agent what the issue is and what behavior I expect solves the issue with a fraction of the effort.
Some claim that the tech debt only keeps increasing and that the result will be unmaintainable. This is not my experience, and I don't think it is theirs either. These claims are often entirely speculative.
I, and I think most experienced developers, can recognize the type of code that incurs a maintenance cost down the line; that will make adding new code take longer. And AI writes such code "relatively" frequently. I love having the AI to write code, but I find it extremely important to review it - to make sure that it's correct, understandable, and not going to be a problem later.
I find it unnecessary for most non-critical code, such as client applications.
I doubt that any supposed future extra effort for the AI to add new code is remotely comparable to the upfront effort of you reviewing the code manually.
I know that this is the case today for native mobile apps, and I speak from hundreds of hours of experience over the last four months on such a project where I stopped reviewing the code.
We are already here today, and this balance is only going to further shift to the point where it is obvious that the hands-on approach is no longer competitive.
Everything about what you're said strikes me as sounding like "I don't bother wearing a seatbelt, because my experience is that I don't get in accidents" .. and also "I don't write automated tests, because I already hand tested my code and it works".
And neither one of those statements is very convincing to me.
And what you said strikes me as speculation not based on actual experience in using AI in this way, with a healthy dose of condescension added.
Anyway, I think we shared our viewpoints, and neither of us is going to change their mind until either my project fails spectacularly, or you change your approach in the future to use AI more autonomously.
I've had bugs the agents can't fix or figure out. Sometimes those involve third-party, proprietary, broken code (read: Windows APIs). Sometimes they just involve complex deployment situation on the client code (I work on desktop apps) where the agent can't figure out what's wrong/makes wrong assumptions/goes nowhere. Sometimes the agent is just very dumb and tunnels vision on the wrong fix.
FWIW, I've also had bugs the agent fixed that I probably never would've figured out without LLMs - LLMs are definitely useful! But I need to keep understanding how the code works so I can take over the reigns when the LLM fails.
I’ve had some luck prompting them to be concise, both in writing and in code, and with code doing an approach where they get it working, write tons of tests, and then refactor for conciseness and readability. All the tests prevent regressions doing this.
Without such prompting and a conciseness and clarity pass you get a slop grenade.
They overall work better with tests, and Rust is a great language for them. Overall they do better with lots of walls and alarms that go off if they mess up. I don’t need nearly as much of this, can mentally simulate it, which is a good “are we superintelligence yet” reality check. Still not even as good as my wet meat brain. But impressive given what was possible even two years ago!
The result is still not as clean as a good programmer but it’s better than the slop grenade you get first pass.
I don't really understand this comment. The simplest way to put pressure on companies to align with their user's objectives is to complain loudly. It's not that HN "assumes" anything, it's just that people call shitty things as they are, in the hope that if enough people agree, it'll create enough pressure for the company to change course.
Will it work? Probably not. But not even trying would be self-defeating.
I don't think it's effective though because you're not actually addressing the reality on the ground. To pursuade someone you need to start with their assumptions.
If your argument doesn't even seem aware of them, then they won't listen.
I guess you could say "who cares, X is evil anyway, might as well try" but it just seems ineffective and frankly a little naive.
AFAIK, the OS-specific Qt themes don't wrap the native widgets, it reimplements them. They are rather thorough though, making sure their OS-specific theme matches the look and behavior of the native OS, which makes it one of the best framework to do something that feels close to native.
As someone using AltStore PAL to install some third-party apps that would never be allowed on the App Store (such as emulators and manga readers), I heavily disagree.
Everything that didn’t turn out exactly like some people wanted is useless or the Worst Thing Ever — which is a boring and cringe rule of the internet.
Each individual proof is zero knowledge, but downloading a _bunch_ of them is an indicator, yes. There's no real way to work around this with the current ZKP scheme. I'd argue ZKP is, in theory, the least worse option of all current age verification scheme.
My personal opinion is that age verification itself is a bad idea, but if we're going to go ahead with it, I'd much rather we use ZKP for it than the current mess of "upload your passport and picture of yourself to dodgy 3rd parties that likely now have your browsing traffic associated with your real name"...
Not necessarily, but it would be difficult to justify why you need to access 1000 porn sites every day, I guess?
The point is that if you build a service that sells age verification tokens, you are doing something illegal. And if you start doing something illegal by gathering said tokens with your own identity, maybe it's not the most clever way to do something illegal?
So the government would limit me to accessing only 999 porn sites a day?
Is it going to be like structuring law, where it's illegal to access 1000 or more but it's also illegal to avoid accessing 1000 or more by accessing a lower number?
The government won't limit you - they can't really know what you use those proofs for anyways, that's the zero knowledge part, and supposedly they'll be necessary for social media at some point, so a ZK proof won't necessarily mean porn use. The only thing the govt knows is how many proofs are being downloaded. And you can bet politicians wouldn't do anything to prevent voting-age you from accessing social media - how else are they supposed to get you to vote for them (or hate their opponents).
Now, a scenario I can see happening is, the ZKP-issuing agency may find a single client downloading several orders of magnitude more proofs than the average citizen, find that suspicious, refer them to the police, who'd get a warrant to investigate said person. Then, the police may manage to connect said person with a proof distribution service. Something along those lines.
Again, I'm personally not a huge fan of the whole age gating, but if we're going to go about it, this is the least worse option. Which is really saying something.
> There is an arrogance in the way of thinking that when you see everyone breaking your arbitrary rules you think to yourself "there is no way my rules are at fault, it's everyone else's fault for breaking them."
It's not really arrogance - it's just a different way to design a system. NixOS wants a few things:
1. An input-addressed dependency system
2. Easy and robust rollback/upgrades
3. Different versions of software/libraries/services coexisting on a single machine.
A lot of this is just impossible to have together with the way software were written, so NixOS made the changes necessary for them. When it became obvious it might be useful to other people, they upstreamed it.
I do agree with you that the whole `#!/usr/bin/env bash` shebang shenanigan is kinda ass (my personal nixos machine has a `/bin/bash` alias because there's not enough time in my life to care about this issue). But I also think that NixOS is right here, shebangs requiring a full path is just a terrible design in a multi-user world. If different users want to have a different bash implementation, they should be able to!
---
Fundamentally, I think the NixOS OS design is actually beautiful. It's taking most of what makes modern phone OS so reliable (read-only system, A/B partitions, etc...), and bringing their ideas in a shape that is coherent with desktop/server OS design.
Can you precisely describe what the goal of #1 is. For flatpaks an app's dependency can be specified with a sha256 of the artifact or a specific commit of the dependency.
Not with NixOS. ld.so is tied to a version of glibc, in ways that can be subtly incompatible. And nixos can have multiple glibc version installed on a single machine.
Besides, it allows for upgrades/downgrades to be done in a way that's much less error-prone.
In theory, ld.so could provide a stable interface to its dynamic loading capabilities independently of glibc. Then glibc would not have to be updated in concert with ld.so. There's no inherent reason that glibc should be the library shipping a dynamic linker - we could easily be in a world where libld was developed independently from libc.
On Windows, things are less modular, so it's less of an issue. That said, there's also weird shenanigans when it comes to the CRT (which can be statically linked) vs ntdll (which provides the actual linker implementation), that can make certain niche features of the linker misbehave (delay loading in particular is weird).
In practice, ld.so is a piece of glibc. If you want to build ld.so from scratch, you have to clone glibc or get a tarball of glibc, and configure and build glibc.
You could make the argument that the glibc developers should split ld.so into its own subprojects. What for? That would just bring the extra responsibility of making sure that variations in glibc version work with variations in ld.so version for whatever reason.
What would happen in practice is that they would keep their version numbers in lock step, and systems integrators would use the same version. While the upstream glibc has to go through a dance of pretending that someone cares about their independence.
still, eventually there's a need to support on one platform different ld.so/glibc pairs (even if they are API/ABI compatible)
it seems nixos could set up a wrapper that invokes the right ld.so based on the executable. though at this point they could probably edit the ELF binaries and patch the fixed path to ld.so when nix is installing the program.
yeah, it seems strange that this needs kernel support. but more eBFP extension points are usually welcome, so sure, why not?
> though at this point they could probably edit the ELF binaries and patch the fixed path to ld.so when nix is installing the program.
That's exactly what they're doing right now - though instead of being "when installing the program", it's "when compiling the program". The problem with hardcoding the path is that it pins the "nix store" (where nix installs all of its programs) to a hardcoded location. If you want to move it, you have to rebuild all your packages - which is suboptimal.
In theory, nix could have a system in place to just patch all binaries when moving the nix store, but that would be incompatible with content-addressed derivation and otherwise break some other nice properties of the nix store.
Not my area, but isn't it really only because glibc doesn't maintain stable interfaces across versions? If it did, you absolutely could use the same ld.so with different glibc versions. But it doesn't, so here we are.
The Windows situation is way different: every process is supposed to link against kernel32.dll, that's the public interface to the kernel. In Linux, glibc is just one of many C stdlib implementations, you can have many versions of glibc on the same system, etc.
Sure, but if you want dynamic loading from your c stdlib (which is defensible IMO), and you want the behavior/implementation to match with the loader, then you need some kind of coupling somewhere no?
You could have a very slim libdlopen that is used by both loader and libc, but I don't really see how that's any improvement/much different.
That's how Windows handles it - the OS libraries are language runtime agnostic and do not export language specific symbols nor need them, and language runtimes are distributed often separately.
So your C stdlib does not have dlopen() (in fact, it's not really part of C stdlib in POSIX anyway!) but you can link with standard OS-level ABI to OS-provided runtime services like "find a library and symbol from it" - AmigaOS did something similar.
$ /lib64/ld-linux-x86-64.so.2
/lib64/ld-linux-x86-64.so.2: missing program name
Try '/lib64/ld-linux-x86-64.so.2 --help' for more information.
!1!
$ /lib64/ld-linux-x86-64.so.2 --version
ld.so (GNU libc) stable release version 2.34.
^^^^^^^^^^^^^^^^
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
There is much more value to be had in making glibc properly backward compatible so you can have a single one that can be used with everything than trying to make it so that you can swap everything around, creating extra complexity and compatibility risks.
> […] in making glibc properly backward compatible […]
You could only do that going forward though, and would be stuck, at least for a time, with the historic versions that still need extra handling. In an ideal world that wouldn't be needed, but in an ideal world you'd not need multiple versions of glibc at all so we aren't there.
Support in the kernel means that it will work even if applied to old packages that for some other reason need a held-back version of glibc. As mentioned elsewhere it also gives the feature to #! directives in scripts too.
How is that more valuable? It comes with two big pitfalls:
- It would still not allow downgrades to work properly
- It would cause glibc/ld.so to have a harder time adding new features, as they now need to worry about incompatible versions being used together
Meanwhile, having different ld.so has many good use-cases, like simplifying development of ld.so itself, allowing them to swapped during updates in ways that are safer, etc...
And the eBPF binfmt support is a rather simple, generic mechanism that is likely to have many other use-cases beyond ld.so. So it's overall a pretty good resolution to the issue?
>It would still not allow downgrades to work properly
If you really need this, then there are options like swapping the linker with an older version or making a hardcoded linker that now points to an older one.
>It would cause glibc/ld.so to have a harder time adding new features, as they now need to worry about incompatible versions being used together
Every language runtime has to care about not breaking compatibility with apps that have been released already. This is not a new or unique problem.
There’s a lot of ways you can hold an engine wrong. Some 3rd party browser (often by security vendors…) have been vulnerable to exploits due to not compiling it with all the hardening options, or injecting vulnerable extensions that expose an attack surface to the webpage, for instance.
reply