Probably oversold here because if you read the fine print, the savings only come in cases when you don't need the bytes in context.
That makes sense for some of the examples the described (e.g. a QA workflow asking the agent to take a screenshot and put it into a folder).
However, this is not true for an active dev workflow when you actually do want it to see that the elements are not lining up or are overlapping or not behaving correctly. So token savings are possible...if your use case doesn't require the bytes in context (which most active dev use cases probably do)*
This has been my observation with self-generated docs as well.
I have seen some devs pull out absolutely bad guidance by introspecting the code with the LLM to define "best practices" and docs because it introduces its own encoded biases in there. The devs are so lazy that they can't be bothered to simply type the bullet points that define "good".
One example is that we had some extracted snippet for C#/.NET that was sprinkling in `ConfigureAwait(false)` which should not be in application code and generally not needed for ASP.NET. But the coding agent saw some code that looked like "library" code and decided to apply it and then someone ran the LLM against that and pulled out "best practices" and placed them into the repo and started to pollute the rest of the context.
I caught this when I found the code in a PR and then found the source and zeroed it out. We've also had to untangle some egregious use of `Task.Run` (again, not best practice in C# and you really want to know what you're doing with it).
At the end of it, we are building a new system that is meant to compose and serve curated, best practice guidance to coding agents to get better consistency and quality. The usage of self-generated skills and knowledge seems like those experiments where people feed in an image and ask the LLM to give back the image without changing it. After n cycles, it is invariably deeply mutated from the original.
Agentic coding is the future, but people have not yet adapted. We went from punch cards to assembly to FORTRAN to C to JavaScript; each step adding more abstractions. The next abstraction is Markdown and I think that teams that invest their time in writing and curating markdown will create better guardrails within which agents can operate without sacrificing quality, security, performance, maintainability, and other non-functional aspects of software system.
> Agentic coding is the future, but people have not yet adapted. We went from punch cards to assembly to FORTRAN to C to JavaScript; each step adding more abstractions.
I don't completely disagree (I've argued the same point myself). But one critical difference between the LLM layer and all of those others you listed, is that LLMs are non-deterministic and all those other layers are. I'm not sure how that changes the dynamic, but surely it does.
The LLM can be non-deterministic, but in the end, as long as we have compilers and integration tests, isn't it the same? You go from non-deterministic human interpretation of requirements and specs into a compiled, deterministic state machine. Now you have a non-deterministic coding agent doing the same and simply replacing the typing portion of that work.
So long as you supply the agent well-curated set of guidance, it should ultimately produce more consistent code with higher quality than if the same task were given to a team of random humans of varying skill and experience levels.
The key now is how much a team invests in writing the high quality guidance in the first place.
The unspoken truth is that tests were never meant to cover all aspects of a piece of software running and doing its thing, that's where the "human mind(s)" that had actually built the system and brought it to life was supposed to come in and add the real layer of veracity. In other words, "if it walks like a duck and quacks like duck" was never enough, no matter how much duck-related testing was in place.
Compilers can never be error free for non trivial statements. This is outlined in Rices theorem. It’s one of the reasons we have observability/telemetry as well as tests.
CTO is typically an executive position, not an IC position.
The CTO at my $500m, YC, series-C startup is not the most technical member of the staff, does not have the broadest technical knowledge, is not the most experienced, nor is he the best in any single technical field in our team.
You misunderstand the role of the CTO in most orgs. His job is to guide technical strategy based on where business is headed. Manage staffing levels, general technical org operations, manage people, be the final arbiter on some org-level technical decisions based on business strategy alignment.
And someone with experience in jobs like Director of Program Management should definitely be able to do that. "technology direction" is not the sole domain of people who write code.
It really depends on the size, it can span from the lead programmer to the lead architect to the lead technical manager to the strategic technical partner and/or technical visionary for the company.
The lesson for me isn't "don't use Pinecone", but more like "did you already max out Postgres?"
In many cases, it is going to save you time by having less infra and one less risk while you're getting started. And if you find yourself outgrowing the capabilities of Pg then you look for an alternative.
I started my career doing ~20 years of C# from pretty much the very beginning (JavaScript even before then!).
Around 2020, I switched into the startup space and quickly picked up TypeScript since that's "what the kids use". It wasn't without struggles, but once I wrapped my head around TypeScript as "shapes for JavaScript", it clicked.
At the time, the startup was undertaking a new product built on Nest.js[0] which looked awfully similar to ASP.NET web APIs which had the benefit of being much more mature, complete, and with one of the best ORMs (EF Core). I suggested it to the team and was a bit shocked by the pushback.
It ultimately inspired me to do a bunch of writing [1][2] on just how similar these two languages are and how they've converged on syntax (no doubt owing to Anders at the helm). Of course, they ultimately still have quite a bit of a gap, but for teams that are working in TS and finding that they are outgrowing it, C# is a very natural choice.
I left that startup after a short stint, but boomeranged almost 3 years later. Company went from seed to series C in that time. End of last year, we started the migration from TypeScript on the backend to C# on the backend and haven't looked back. The toolchain is far simpler and stable, the "default" ORM is far more flexible and powerful, the more rigorous type chain seems more conducive for agent-driven coding. Team adapted relatively quickly within ~4 weeks.
I can't say for sure because I don't know the full situation, but I've heard a similar story a few times and IMNSHO looking at it as "they made the wrong choice initially" is off because it assumes that a C# team could have delivered the initial version in the timeline required to get to the the next level of project.
If they had a team that knows nest and can iterate fast with it that's the perfect choice. I've worked at multiple agencies over the years and was mostly in C# teams. Whenever a C# team got on greenfield/startup projects it ended up being a shit-show of self inflicted slowdowns/over-complications. I've seen 3 projects where in large part due to slow development of the MVP the project missed investment window or ran out of funds.
From my experience Node/Rails teams were much more capable of delivering shit that works. It would eventually have a bunch of problems that would be non issues by default on a different stack like ASP.NET, but the difficulty of getting to that point and realizing that just being in that situation is a win is what most engineers miss.
> ...because it assumes that a C# team could have delivered the initial version in the timeline required to get to the the next level of project.
I linked the Nest.js project because you can see how similar these two are[0] with Nest.js leaning into the OOP aspects of TS at the very core. Controllers and services are classes in Nest.js, for example. It uses a somehow more complicated DI system than ASP.NET. It is Spring Boot or ASP.NET, but without the maturity, performance, and ergonomics.
The team had developers that had done C# before and later hires also included former C# developers.
TS itself was new for the team at the time and several mistakes were made along the way resulting in a "dual-ORM" situation (Prisma + Drizzle; both with their faults) that ends up sapping a lot of productivity (one of the drivers to move to C#).
Hmmm. I have a different take there: when you are young and wild, you achieve stuff because you think later and instantly produce code. When you turn older, you do it the other way leading to your example.
In the early 2000s I have been in a startup and we delivered rapidly in C# as we did in PHP. We just coded the shit.
I think what you said is a healthy progression : write dumb code -> figure out it doesn't scale -> add a bunch of clever abstraction layers -> realize you fucked yourself when you're on call for 12 hours trying to fix a mess for a critical issue over the weekend x how many time it takes you to get it -> write dumb code and only abstract when necessary.
Problem is devs these days start from step two because we're teaching that in all sources - they never learned why it's done by doing step one - it's all theoretical example and dogma. Or they are solving problems from Google/Microsoft/etc. scale and they are a 5 people startup. But everyone wants to apply "lessons learned" by big tech.
And all this advice is usually coming from very questionable sources - tech influencers and book authors. People who spend more time talking about code than reading/writing it, and earn money by selling you on an idea. I remember opening uncle bobs repo once when I was learning clojure - the most unreadable scattered codebase I've seen in the language. I would never want to work with that guy - yet Clean Code was a staple for years. DDD preachers, Event driven gurus.
C# is the community where I've noticed this the most.
Same experience here. C# might have superior tooling, performance, whatever but the OOP baggage is too heavy. In theory you can write something else than a giant over-complicated, over-abstracted pile of OOP nonsense in C#, but every team I've seen has.
You can write very functional C#. Our codebase is a mix with some aspects being functional (`ErrorOr`[0] being a big part of it as well as `OneOf`[1]) and OOP. Our core, common services all return `ErrorOr` to allow fluent call chaining at the top of the stack (controllers).
Modern C# features like `switch` expressions[2] (not `switch-case`) and pattern matching mean that it is possible to write very terse, expressive code. Extension members and methods[3] go a long way as well by making the builder pattern easier to implement.
Overall, it's up to the team to make use of the tools provided by the C# team. You can write C# in a very OOP heavy way (as is possible with TS in the case of Nest.js); you can also write in in a very functional way given many of the functional features adopted from F# over the years. It's up to the team.
That's a perfect example of making it overcomplicated, just in the FP direction.
C# uses exceptions for error handling. It has it's own control flow primitives. C# developers know how to work with it, everything else uses it. Why would I want to pull in a randos GH DSL and types to pretend I'm writing F# when I can just use F# that has first class support for this ?
C# is already very functional when it comes to Linq. In real world use, this just feels like extending in the same directionality while still preserving familiarity. Whereas F# does feel like an entirely different language.
Learning C# when you know TS is like learning Portuguese when you know Spanish.
> In theory you can write something else than a giant over-complicated, over-abstracted pile of OOP nonsense in C#, but every team I've seen has.
C# syntax is fine, but has a rotten[1] culture/conventions. I suppose it makes sense that Microsoft's "Java-killer" became enterprise-y, with the same over-engineered indirections.
1. IMO - I find it very unpleasant and never allowed myself to Internalize the IConventions out of spite. YMMV.
Because EF's default behavior implements Unit of Work, a LOT of the complex transactional spaghetti ended up disappearing when we switched.
This aspect of EF is highly underrated for complex entity graph mutations.
EF makes the 90% use case easy and the 10% case possible with very little pain. The interceptors, global conventions, and other extension points are an enabler of complex behaviors that are still transparent to most of the team.
> Because EF's default behavior implements Unit of Work, a LOT of the complex transactional spaghetti ended up disappearing when we switched.
I have never felt more understood by a fellow HN user. I think I know the exact spaghetti you are talking about, and I agree with you 100%. I wish EF could create (SQL) views, but it's not really any issue considering I can just use raw SQL to accomplish the same thing.
> complex entity graph mutations.
I'm too dumb to know what those words mean together. But I going to assume it's something to do with complex entities. If so, I completely agree as well. I sometimes harness the powers of the Dark Magics where my domain entities and database entities are the same objects (I'm not one of those DDD people either). Thanks to EF, I've been able to create some complex objects that really have cut down on a lot of useless objects I used to litter applications with.
I assume the startup wasn't also leveraging typescript heavily on the frontend, that tends to shift the weight in its favor. Having one set of tools to use across everything, being able to share logic and types without needing to go through lossy translation layers, and giving (especially small) teams better flexibility to move people around is a huge benefit.
But the reality is that at some point, your FE and BE teams will diverge anyways and we use an OpenAPI spec as the contract (Nest.js, not Next.js).
So there was no benefit to using TS on both ends; only pain on the BE.
If a team is going to ship an OpenAPI spec and run it through a transformer, then it changes the selection criteria for a BE language:
- Easy for a TS team to adopt; similar core semantics like `async/await`, exception handling, etc.
- Flexible and pluggable OpenAPI spec generation for edge cases and advanced scenarios
- Excellent ORM to improve productivity around CRUD
- Good tooling
- Extensive docs, platform maturity, but modern language features
C# meets all of those in ways that no other language and platform does.
I can't speak to your use case, but for past projects I've not only wanted to share types but actual logic. For instance, if you want instantaneous validation on the frontend, while applying the same logic on the backend with submitted data. In many instances that would be simple and maybe even serializable, but in my case I was working with architectural data that had extremely detailed logic for what was and was not valid.
Using a TS validation library like Zod gives you both the shared validation code for both the frontend and backend, the TS types, as well as the json schema for openapi. It's a triple whammy too good to ignore. Especially as a small team it is huge leverage enabling you to go further faster.
Incidentally, there's a key word here: "promise" as in "futures".
This is core of a system I'm working on at the moment. It has been underutilized in the agent space and a simple way to get "proactivity" rather than "reactivity".
Have the LLM evaluate whether an output requires a future follow up, is a repeating pattern, is something that should happen cyclically and give it a tool to generate a "promise" that will resolve at some future time.
We give the agent a mechanism to produce and cancel (if the condition for a promise changes) futures. The system that is resolving promises is just a simple loop that iterates over a list of promises by date. Each promise is just a serialized message/payload that we hand back to the LLM in the future.
I ran into a new problem today: "reading atrophy".
As in if the LLM doesn't know about it, some devs are basically giving up and not even going to RTFM. I literally had to explain to someone today how something works by...reading through the docs and linking them the docs with screenshots and highlighted paragraphs of text.
Still got push back along the lines of "not sure if this will work". It's. Literally. In. The. Docs.
That's not really a new thing now, it just shows differently.
15 years ago I was working in an environment where they had lots of Indians as cheap labour - and the same thing will show up in any environment where you go for hiring a mass of cheap people while looking more at the cost than at qualifications: You pretty much need to trick them into reading stuff that are relevant.
I remember one case where one had a problem they couldn't solve, and couldn't give me enough info to help remotely. In the end I was sitting next to them, and made them read anything showing up on the screen out loud. Took a few tries where they were just closing dialog boxes without reading it, but eventually we had that under control enough that they were able to read the error messages to me, and then went "Oh, so _that's_ the problem?!"
Overall interacting with a LLM feels a lot like interacting with one of them back then, even down to the same excuses ("I didn't break anything in that commit, that test case was never passing") - and my expectation for what I can get out of it is pretty much the same as back then, and approach to interacting with it is pretty similar. It's pretty much an even cheaper unskilled developer, you just need to treat it as such. And you don't pair it up with other unskilled developers.
The mere existence of the phrase "RTFM" shows that this phenomenon was already a thing. LLMs are the worst thing to happen to people who couldn't read before. When HR type people ask what my "superpower" is I'm so tempted to say "I can read", because I honestly feel like it's the only difference between me and people who suck at working independently.
As someone working in technical support for a long time, this has always been the case.
You can have as many extremely detailed and easy to parse gudies, references, etc. there will always be a portion of customers who refuse to read them.
Never could figure out why because they aren't stupid or anything.
Way easier to set up, centralized auth and telemetry.
Just use it for the right use cases.
reply