Hacker Newsnew | past | comments | ask | show | jobs | submit | wellpast's commentslogin

Two months ago I had a water pipe burst in my house.

Now I am in the claims process with the large insurance co., using Claude to basically run my side of the claim — reading the policy, organizing and filing the ALE receipt scans, reading and notifying me of messages from the agents, calling out when they are delaying too much or not fulfilling their promises, challenging their decisions on what to cover based on the policy, keeping a log of all of our correspondences, logging work estimates and invoices, I cant imagine doing all this tedious work myself—w/o Claude I’d probably just rollover fatigued.

Does my adjuster like this? On the one hand, I’m giving her incredibly tidy and documented receipts and logs, which I am sure is saving her time.

On the other hand, Claude is calling out her BS and delays, which have been atrocious—so much so that Claude is now adamant that she has gone too far and that I should be filing a complaint with the state insurance bureau.


    > On the other hand, Claude is calling out her BS and delays, which have been atrocious—so much so that Claude is now adamant that she has gone too far and that I should be filing a complaint with the state insurance bureau.
My first guess: She is wildly overworked and that is an insurance company strategy.

This is an interesting customer strategy. Have you escalated your concerns to her management? Have you explained to her that you are considering filing a complaint with the state insurance bureau? It's the ol' stick vs carrot. I say: Start with the carrot. If that doesn't work, back to the stick!


Of the two options you presented, escalating to management or filing a complaint, which is the carrot?

Good question: They are two types of sticks: little and big.

A threat to use the stick is not a carrot.

Definitely the hallmark of junior. Obsession with code deduplication as the highest pri when it’s quite low among others.


Well I have seen a lot of „expert beginners” who have years of experience on paper but fight tiny duplications like their life depends on it.

„How Software Groups Rot: Legacy of the Expert Beginner”.

https://daedtech.com/how-software-groups-rot-legacy-of-the-e...


Thank you for this link.

I have recently fallen into a job at a small company that really seems to have this culture. Thankfully, I'm only going to be here for a year and a half or so (fixed term job for working holiday visa), but I'm trying to be really aware of how its impacting my career development.

There is no automated testing, no meetings, seemingly no code review process, no standardization of schemas for files that are passed between different applications, all jobs are run on on prem desktop workstations.


Simply put, even solipsism is unfalsifiable.


Not even to optimize for, but to write correct programs you really need to understand the runtime which is usually broader than the syntax.

All Clojure (lisps) do is remove the stupidity of syntax.

Even if syntax is the minor thing, why wear a stupid, uncomfortable shirt while running when you could wear one so comfortable you scarcely feel it?


Well I agree, that's why I use Elixir and not its underlying Erlang.

And I agree that most LISPs remove the stupidity of syntax. Very true.


Clojure was explicitly designed to be dynamic. It’s a feature, not a bug.

https://clojure.org/about/dynamic

Until you get better at not making mistakes that the training wheels of a static type system “protect” you from, lean into the REPL as a means to build up small correct expressions into larger ones.


"Until you get better" is such an arrogant take.

It's not just about skill. It's about maintainability, ease of refactor, and modeling invariants in your code in a way that they can be checked by the machine (the compiler) without every single developer having to maintain them in their head.

Clojure even knows this is an issue and many people use `spec` to sort of retrofit static typing.

Dynamic typing was, is and always will be a mistake. There is nothing you can do with dynamic typing that you cannot do with a sufficiently powerful static type system - and it doesn't have to be something absurd like Haskell's. You basically just need structural typing and type inference and some type-level programming constructs.

The worst part about Clojure is the community. Rich Hickey has cult-like status and the only thing clojurians can do is parrot his inane commentary.


I too agree that "until you get better" isn't a good take. To err is human, and even the most experienced developers make mistakes.

That said, you don't get static typing for free. As with many things it's a trade-off: you catch some errors at compile time in exchange for working within the confines of the type system. The ultimate hope is that the time you spend fiddling with types is going to be less than the time you spend debugging type errors.

> There is nothing you can do with dynamic typing that you cannot do with a sufficiently powerful static type system - and it doesn't have to be something absurd like Haskell's. You basically just need structural typing and type inference and some type-level programming constructs.

Haskell doesn't have a complex type system for no reason; it's necessary to encompass everything it wishes to do, and even then it's not as flexible as a dynamically typed language.

For instance, how would you statically type Clojure's `assoc` function? It's not at all trivial if you want to retain the type information of the keys and values.


The problem with your counter-argument is that it hinges on a false premise: That you need or even want a function like `assoc` which is polymorphic over everything. It's an extremely overloaded function which does a lot of things at once, and in many circles and arguably in general within the realm of software design, this is considered a smell.

In practice, what you want is something that allows you to do this safely for the concrete type you're working with. If you want an abstraction that covers all of it, there are ways to achieve this in a type-safe manner, such as traits/type classes. Even in clojure, you're not working with everything at once all the time. You are working with a record, or a vector, or whatever. The fact that you can use one function for all of them is mostly just needless cleverness. In Clojure, you have to keep the type of the data you are working with in your head at all times, because even though `assoc` "just works" for many cases, that's not true in all cases. It will happily insert an integer key into a record without issue, which may or may not be waht you want. But you can also try to insert an atom key into a vector, which then crashes loudly. This is clearly an asymmetry in the abstraction.

Moreover, pointing out that Haskell cannot do what you'd want to do in this case doesn't make a lot of sense. I mentioned Haskell precisely because its type system is extremely powerful and complicated to understand for a lot of people, but still doesn't achieve the kind of flexibility we are looking for - it lacks row polymorphism.

To answer your actual question: Typing a function like that for the individual cases is bordering on trivial in a language such as typescript. For the record case, you don't even need it, because in practice, you get the correct type inference for free by just spreading one object into another.


I don't see an asymmetry in the abstraction. Both vectors and maps are associative structures - you can assign a key to a value - the only difference is that vectors have a more constrained keyspace (i.e. ordered, consecutive integers starting from zero).

But that wasn't really my point. Even if we limit `assoc` solely to maps it would still be difficult to type effectively.

For instance, suppose we have some code like:

    (let [m* (assoc m :number 3)]
      (:number m*))
We can see that the return type of this expression is obviously an integer, but what is the type of m*? How do we type m* such that (:number m*) can be inferred to be an integer by the compiler?

Most statically typed languages sidestep this problem: instead of using an open data structure like a map, a closed structure like a record or class is used instead, and these structures must be explicitly typed by the user.

The problem with this approach is that now every record is specific and bespoke. You lose access to all the general-purpose functions that operate on generic data structures, and as records and classes are closed, you also lose the ability to extend them.

This is the ultimate problem with static type systems: you're trading capability for safety. If you're programming within a static type system, there are options that are simply not available or feasible to use.


> I don't see an asymmetry in the abstraction. Both vectors and maps are associative structures - you can assign a key to a value - the only difference is that vectors have a more constrained keyspace (i.e. ordered, consecutive integers starting from zero).

The asymmetry lies in the fact that it's an overloaded function that's supposed to do the right things every time, but in some cases, it does what is arguably the wrong thing, silently, and in others, it refuses to do the wrong thing and fails loudly. It's better that it fails loudly, of course, but the point is that the ergonomics of the abstraction is lessened because you can't just assume it will work. You effectively have to keep the types of all the things involved in your head and/or trace them to ensure that you don't run into a crash.

> We can see that the return type of this expression is obviously an integer, but what is the type of m? How do we type m such that (:number m*) can be inferred to be an integer by the compiler?

This is trivial in TypeScript. You can see it in action here: https://www.typescriptlang.org/play/?#code/MYewdgzgLgBAtjAvD...

  const m = { name: "weavejester", active: true };

  const mStar = { ...m, number: 3 };
  //    ^? const mStar: { number: number, name: string, active: boolean }

  const x = mStar.number;
  //    ^? const x: number
> This is the ultimate problem with static type systems: you're trading capability for safety. If you're programming within a static type system, there are options that are simply not available or feasible to use.

This is just not true. It's true for some certain specific static type systems, but not true in general, and that brings me back to my original thesis: You just need a sufficiently capable type system with the right properties - structural/row polymorphism, ish, plus type inference. And also my Haskell point: it doesn't have to be an incredibly complicated type system that is beyond mortal ken. TypeScript is already doing this and it's arguably one of the most used programming languages on earth.


> You effectively have to keep the types of all the things involved in your head and/or trace them to ensure that you don't run into a crash.

You make this sound difficult, but in practice type errors are rare in Clojure and generally caught in the REPL or by tests, since the moment you go down a branch with a type error an exception is thrown.

Contrast this to errors caused via mutable state, which are usually far harder to track down, because the failure condition is more specific.

> This is trivial in TypeScript.

In the example you give you're omitting assoc entirely, which defeats the point. I'm using assoc as a minimal example, but the same principle applies to more complex functions, so replacing assoc with the equivalent expression doesn't tell us whether or not we can effectively type a function that deals with maps.

So lets try doing this properly. At minimum we need something like this:

    type Assoc<M extends object, K extends string, V> =
        Omit<M, K> & Record<K, V>;

    function assoc<M extends object, K extends string, V>(
        m: M, k: K, v: V): Assoc<M, K, V> {
      return { ...m, [k]: v } as Assoc<M, K, V>;
    }
(Note that we need to perform an explicit cast in order to inform TypeScript of the type of the key.)

However, this produces some rather messy types consisting of nested Assocs. In order to get back to something a human can read, we can use an additional Simplify type to force the type system to reduce it back down into an typed object:

    type Simplify<T> = {[K in keyof T]: T[K]} & {};

    type Assoc<M extends object, K extends string, V> =
        Simplify<Omit<M, K> & Record<K, V>>;

    function assoc<M extends object, K extends string, V>(
        m: M, k: K, v: V): Assoc<M, K, V> {
      return { ...m, [k]: v } as Assoc<M, K, V>;
    }
(The empty `& {}` intersection forces normalization, providing a cleaner reported type.)

We're still not done, though, as if we want the same type checking that a class has, we need to ensure that a key cannot be overwritten with a value of a differing type. So we'll type the value argument as well to ensure it matches the type of an existing value within the map:

    type Simplify<T> = {[K in keyof T]: T[K]} & {};

    type Assoc<M extends object, K extends string, V> =
        Simplify<M & Record<K, V>>;

    type AssocValue<M extends object, K extends string, V> =
        K extends keyof M ? (V extends M[K] ? V : never) : V;

    function assoc<M extends object, K extends string, V>(
        m: M, k: K, v: AssocValue<M, K, V>): Assoc<M, K, V> {
      return { ...m, [k]: v } as Assoc<M, K, V>;
    }
So this is possible to type in TypeScript (to its credit), but is it "trivial"? And is this type signature significantly less complex than one might find in Haskell?


“Until you get better” at pedaling, training wheels can help.

It’s not an arrogant take; it’s arrogant to think you know static typing is a requirement for developing software well.


It's not about "knowing" anything. It's about admitting that humans are fallible meat computers that can't hold invariants in their head across thousands or millions of lines of code and possibly an exponential number of interactions. It's using the technology we are capable of building to help us because it's the obvious thing to do. The notion of dynamic typing as an attractive programming model hinges entirely on the hypothesis that it lets you somehow express things that you need or want to be able to express that static typing prevents you from doing, and that is demonstrably false. The `assoc` example above is a perfect example.


> that can't hold invariants in their head across thousands or millions of lines of code and possibly an exponential number of interactions.

There’s your problem. And static typing won’t save you either.

The skill is not that, it’s the ability to compose and evolve systems such that you don’t have to hold so much state in your head.

(Btw that property can hold for a million LOC codebase.)


It’s both.


I’m willing to be wrong but this industry-wide emphasis on AI creative/coding workflows seems way over-engineered.

Ime successful creative execution looks like micro-iterations where each output informs the next creative move.

I can build something incredibly fast from essentially caveman grunt instructions through an LLM harness, iterating as I go.

Optimizing for feeding a huge plan to an agent sounds to me like a net waste of time. And looking over the shoulder of industry peers trying to do this, I don’t see their outputs or throughput some remarkable improvement over what I can produce with minimal fanfare usage.


LLMs are too flaky for high quality code. On tougher problems it's very common for an LLM to contradict itself and run in circles. It simply doesn't know what the right thing is, but on each turn it is super confident to do the right thing.

Maybe I've chosen hardmode to learn C with LLM assistance, plus my pet project turned out to be a bit less trivial then anticipated. But I know that I have to think three times about my choices how to deal with C problems and seeing how a LLM struggles to give reasonable answers is a a huge red flag and forces me to think about it a fourth time.

Doing all this with a fast autonomous workflow with just little user guidance is asking for trouble.


It’s not just you. My last dumb pilot program making a Pocket clone in Python also got stuck in a loop regularly, which should be its strong suit...

I suspect that the “right” way to use LMs in coding, including accounting for focus, control, and costs is not a settled debate. We probably haven’t even seen the best ideas yet. But I’m really dislike the maximalist approach.


> Maybe I've chosen hardmode to learn C with LLM assistance

May you speak a little more about how you're approaching this? I was thinking of doing similar


Well my process was/is quite messy. It started out as an experiment for a desktop app (which turned out to be a bit more complex than anticipated), mostly using copy and paste to try different approaches for a working prototype.

The progress is bottlenecked by how things are done with C. Most features went through an considerable LLM research phase to find the sweet spot how I want to solve it. In the feature phase I've coded more and more on my own and used the LLM as a reviewer on the critical parts.

I know a few things about C, but my knowledge is very passive, but there is already a path carved in my brain from influential C programmers you find on yt and blogs. That being said, LLMs will actively gatekeep and bullshit you about C and system architecture. You need to drill down hard, from different angles i.e. what is efficient and what is pragmatic or you question about how hardware actually works. They still may have blind spots and casually keeping things from you. Naively asking for code snippets will be tutorial style and often backfires, not unseen that they rant about their own code in another session. So you need to maintain a healthy balance of knowledge from trustworthy humans and the quickly available, but potentially inaccurate/incomplete LLM knowledge. Be critical, you are the captain.

LLMs are not bad at reviewing C code. They catch a lot of noob mistakes and are more helpful then compilers. But again they tend to produce tutorial solutions. They litter everything with malloc, which is something I am trying to avoid. I've not touched arenas yet, I want to fall flat on raw C to value them. They can teach you arenas, but I suggest to cross check with the real world, especially because you want ergonomics and correctness on that behalf.

Most C adjacent topics are easy to pick up on the go. Build systems, debugging, API vs ABI, static vs dynamic linking, macros, etc. A seasoned programmer can get a quick overview and ad hoc help, for things that are often buried in mediocre docs.

Also if you happen to come from more higher level languages or dynamic languages. Expect C to require easily 5x more code for literally anything.

If you prefer to learn the language more structured, work your way towards learning how to create data structures and algorithms in C. Make a clear distinction between static and dynamic allocation and learn along the road how C/hardware wants you to deal with memory.


Yeah it's wild watching so many people decide waterfall is great all of a sudden.


Never mind stumbling into proper engineering principles like having documented, testable requirements specifications.


I”ve been pretty happy with this side effect of the agentic coding bubble.


As a non-tech engineer (mechanical, trains) it's fascinating seeing what is essentially the "not real engineers" SWE crew finally pay the piper because they've invoked what is in essence a non-compliant, cost-focused subcontractor and now need all of the same engineering rigours they never previously understood.


Every large project in the coming back to waterfall. While the problems are certainly known and it was ultimately developed as a straw man, everything else ends up working worse. That said, you shouldn't be thinking pure waterfall as it's drawn up as a strawman, but rather a waterfall variation with feedback loops. But in the end, in very, very many cases, you have to know an end date in order to get things done because so many other things depend on you being done at the same time. If something is going to get done sooner you can't use it anyway without all the other pieces.


ITT we discover that project managers actually serve a purpose and not all of them are the stereotypical useless roles Dilbert riffed on.


"waterfall variation with feedback loops" lol next we're going to have "agile where you plan everything up front"


Pretty much what most agile ends up. Plans are worthless but planning is a valuable exercise. (Attributed to various generals)


100% agree. Took me ages of working with the agents to circle back around this, which was the best way to get work done before AI automation anyways.


For me it's usually that I start with a single agent, but then I won't have anything to do while it is churning and I have other ideas/features that keep building up that I want to do, so I need to scale, and while I'm scaling I need to start to have those workflows, so eventually I end up with many agents, most which are autonomous working on their own worktrees, but I will have a specific agent that I will talk to more iteratively.

So e.g. I may have 1 agent that I ask and iterate on with directly, and 9 agents that work separately on their own.

I will utilize this 1 agent on features I care most about and want to guide and iterate on in as much detail as possible.


I agree. I have gotten an incredible amount of work done iterating with 5-30 minute long agent tasks. But it requires I stay engaged, and not go chill on the beach, which I guess is a lot of agentmaxxers’ goal.


I suspect that letting agents spin away unattended for long stretches of time will become less and less popular as more and more companies blow their token budgets and start requiring some answers to difficult questions before agreeing to further loose the purse strings.


The trick is to have 5 of those huge plans running in parallel.


you do not need micro iterations. you can set macro goals and let the agent/LLM/model whatever you want to call it figure it out.

it works.


> When I think about a programming problem, I think in terms of the sequence of instructions I need the computer to do, and the memory locations that can hold the information the computer needs to track.

You’re almost there. Just stop thinking about the sequence of instructions. Focus on the information half (the values) that you need to produce.


In Austin we have most notably Terra Toys and Toy Joy.

Terra Toys is 50 years old, its space shows it. Hand-written recommendations and prices. The employees demonstrate and play with the toys, welcome you genuinely as you enter. The toys seem curated for actual fun not schtick. I went in last week and it was popping. It’s an experience.

Does Main Street need to focus on experience to survive? If so, how does it monetize experience if selling items isn’t the first focus now?


> HTML is marking up the meaning of the document.

Is this true at all anymore, except for SEO optimized sites/content?

For apps, it’s all layout, isn’t it — and HTML, JS, CSS have all evolved heavily to support UI-first, haven’t they?


One way to potentially discourage or curb AI-edited/written is integrate AI into HN so that your submissions get recommendations based on HN post guidelines such as “consider tone”, “substance” etc.

Then less motivation to jump out to external LLM to even get comments on your content which can temptingly lead to editing/generation.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: