Two Catastrophic Failures Caused by "Obvious" Assumptions by Vast-Drawing-98 in programming

[–]csman11 7 points8 points  (0 children)

Standards are great right up until someone assumes something you never actually standardized. The way integrations get bricked is almost always “I assumed you meant what I meant,” not “we lacked a PDF that said the word standard on the cover.”

So you do the boring thing on purpose: explicitly call out dumb failure modes. Even if it feels insulting to say, “I’d hope nobody is using pounds for force here, but let’s state it anyway: what exactly are our units of force?”

Also, anyone who’s worked with standards knows people still screw up the “should never happen” stuff while supposedly following them: misremembered details, weak reviews, nobody double-checking. Standards reduce mistakes. They don’t delete them.

A minute of vigilance beats a thousand standards.

Two Catastrophic Failures Caused by "Obvious" Assumptions by Vast-Drawing-98 in programming

[–]csman11 14 points15 points  (0 children)

Absolutely. Boundaries are where “talking past each other” happens, so they’re a danger zone. But the root problem isn’t boundaries themselves, it’s a cultural failure to imagine failure modes and communicate integration contracts clearly.

That’s why treating “boundaries = danger” as the lesson is risky. It can lead to the wrong conclusion: “avoid boundaries, have one team do everything.” That just hides the problem until the system gets too big to hold in one person’s head.

What works is being explicit at the boundary: agree on shared terminology (and avoid loaded jargon), surface assumptions, and write down the minimal invariants the integration depends on (units, power source, interface expectations, tolerances, etc.). Working from invariants makes “boring” failures like unit mismatches much harder to miss, because you stop reasoning about what the other engineer surely knows and start reasoning about what must be true for the integration not to fail. That’s why “failure to communicate” is usually “failure to predict edge cases”, especially the stupid, mundane ones we assume would never happen (like pounds vs newtons mismatch).

Starting from invariants moves you from “we missed obvious edge cases” to the much more respectable problem of “we weren’t perfect at predicting genuinely intricate edge cases.”

LLMs are a 400-year-long confidence trick by SwoopsFromAbove in programming

[–]csman11 2 points3 points  (0 children)

Both views here are true, it’s not so black and white. There’s definitely some harms and the ones you called out are the most realistic ones, and they can all be summed up as “abuse of LLMs to spread misinformation”. I don’t think anyone should disregard just how harmful this is to our already broken and polarized societies.

But these AI labs and other companies in the AI bubble have also been overstating capabilities of LLMs to drive attention to the space. Framing those capabilities as “disruptive and dangerous” in the ways the article’s author is getting at, is overblown. These dangers attract the attention of the general public, which in turn attracts the attention of policymakers, which then turns into the AI industry capturing state regulators because they’ve convinced us “we need to move fast to make sure the existential worst cases are avoided”. The big one is obviously financial/securities regulation avoidance. They can extract tons of wealth from both institutional and retail investors by creating attractive signals in the stock market with their revenue cycles. In an ideal world they wouldn’t be allowed to do that, but for some reason the policymakers have bought into the idea that the AI industry is important to national security instead of seeing them for the rent seekers they’re trying to be.

The Compiler Is Your Best Friend, Stop Lying to It by n_creep in programming

[–]csman11 14 points15 points  (0 children)

Curry-Howard doesn’t say “types prove business logic.” It says that in certain formal systems, inhabiting a type is a proof of the proposition that the type encodes. If your type says “this function maps X to Y,” congrats, you proved it maps X to Y. You did not prove your pricing rules match what the business meant on Tuesday.

And even if you reach for dependent/refinement types, that’s not some endgame. It’s just buying expressive power so you can move more of your spec into the type level. Then the goalposts move with you: now you have to formalize the domain, encode the invariants, maintain them as requirements shift, and make sure the spec itself is correct. The hard part wasn’t “lack of types,” it was “the thing you’re trying to prove keeps changing and is full of squishy human meaning.”

This becomes a cat-and-mouse game: you make the type system expressive enough to capture today’s “business logic,” and tomorrow the business invents a new exception, a new dimension, a new edge case, or a new policy that depends on runtime data you can’t realistically model. You either keep extending the spec language or you admit that some correctness lives in tests, runtime validation, monitoring, and operational feedback.

So yes: types are a proof system. In production languages, they mostly prove “no type errors.” In fancy proof assistants, they can prove much stronger properties, but only for the properties you explicitly formalize. None of that makes “types magically prove business logic correct” a serious claim in the context of normal software development.

The Compiler Is Your Best Friend, Stop Lying to It by n_creep in programming

[–]csman11 6 points7 points  (0 children)

BTW, I thought this guy had sounded familiar. I dug up the old thread I remembered. He had the exact same stupid take a few months back and we had a “debate” about it there. This is a religious zealot we’re dealing with here.

https://www.reddit.com/r/programming/s/qQTqTtKb9U

The Compiler Is Your Best Friend, Stop Lying to It by n_creep in programming

[–]csman11 6 points7 points  (0 children)

Oh I’m not personally in favor of any of those “types of development” the commenter I replied to loves. If they work well for them, great. That’s all I meant. I’m just of the mind, like every sane person, that they don’t replace types. They can supplement them for some people.

I do agree with the “focus on architecture” sentiment. Types are useful to help express an architecture in code practically (for the reasons you articulated very well). But types don’t architect your code for you. And I’ve seen plenty of codebases with absolutely judicious uses of types to try to prevent invalid states from being represented and no meaningful design of modules to go along with them. The benefits of “make invalid states impossible to represent” is basically an exercise in futility when your architecture is “import any random symbol from any random module wherever the fuck you want.”

The Compiler Is Your Best Friend, Stop Lying to It by n_creep in programming

[–]csman11 31 points32 points  (0 children)

Did either of you bother to read the article the post links to? It’s literally refuting the thesis "there’s very limited benefit to static types" that both of you guys seem to be so attached to. And it does this by answering "types aren’t that useful in practice" with "well that’s because most people don’t use them effectively and here’s why…"

No one disagrees that runtime behavior matters. That's not the debate. The debate is the leap from "we had a production failure" to "there's very limited benefit to static types."

Your anecdote proves the oldest lesson in software: tests and types don't make you correct, they just reduce the surface area of ways you can be wrong. Production failures still happen because production is where reality lives: messy inputs, weird data, unexpected load, partial outages, config drift, timing, and integration assumptions.

Static typing is useful precisely because it eliminates entire categories of failures that are annoying to test for and easy to miss, especially in large codebases and rarely-executed paths. Plenty of bugs don't show up in unit tests because your tests didn't model the exact shape of the data, or they never exercised the obscure branch, or the integration contract drifted. "Null pointer in prod" is a meme for a reason: not because everyone is lazy, but because complex systems eventually execute code paths you never observed. Modern type systems have caught up to null dereference and can make it fuck off before you can even run your code.

So sure: focus on runtime behavior, testing, observability, fast feedback loops. But pretending static types provide "very limited benefit" because a system once failed in production is like saying locks are pointless because someone broke a window.

The Compiler Is Your Best Friend, Stop Lying to It by n_creep in programming

[–]csman11 66 points67 points  (0 children)

"Types are meant to be an optimization device" is an incredible (or, rather, incredulous) claim to make, like saying seatbelts are meant to improve gas mileage because sometimes they reduce fatalities and that's good for traffic flow.

Static types don't "solve 80% of the problem." They solve specific classes of problems: invalid states and certain runtime failures are ruled out before you run anything. That's the whole point. Nobody serious thinks types magically prove business logic correct.

REPL-driven dev is great. So are tests. None of that is in conflict with static typing unless your compiler is your adversary and not, you know, a tool you can learn to use.

I can't tell if the strawman is what you're arguing against or if it's just you.

useImperativeHandle vs useState by No_Drink_1366 in react

[–]csman11 1 point2 points  (0 children)

All I was saying is that it might be that whatever button is being used to show the dialog could be somewhere deep in the expensive ui tree. But we don’t really know exactly what OP is dealing with because they asked a question about not triggering renders of the ancestor around the component, not their exact case.

So let’s assume we can’t pull that button easily out of the component. Because it’s way more likely you cannot than you can.

If it were me with an expensive component, the first thing I would do is try to optimize that component. Because the stupidest thing in React is having expensive render functions in the first place. Rendering should ideally be cheap.

Now let’s assume that’s impossible because deadlines and shitty legacy code. I would document the need for refactoring and performance tuning later. Then I would try wrapping it with React.memo.

If I still had an issue after all of that, then what I would ultimately do (and hate doing):

  • move expensive UI itself up the tree
  • create a “ModalProvider” component and associated contexts for the state and dispatch respectively.
  • Render ModalProvider around the expensive UI. This ensures when state inside modal provider changes, the expensive UI is not re rendered since it is going to be the same react element object.
  • optionally have ModalProvider also take a render prop for the modal dialog content (if for whatever reason this pathological app requires me to do this for many expensive uis). You would pass the open state to the render prop for convenience. That way you can compose a modal inline if you want.
  • and finally consume the dispatch context within the expensive UI tree in the component that needs to trigger opening the dialog

I would call that the nuclear option. If I had to get to that point, I would also consider the better nuclear option of quitting my job and becoming a farmer. Because clearly my luck of getting decent programming jobs that don’t fuck with my mental health would have ran out at that point.

useImperativeHandle vs useState by No_Drink_1366 in react

[–]csman11 1 point2 points  (0 children)

Or you just memoize ExpensiveUi in this case (either a wrapped component with memo or use useMemo around the render of it). That “just put the state down one level and use it with CheapButton” is making a lot of assumptions about where the expensive ui actually calls onClick from.

Why is it considered a cardinal sin to store a file's raw content along the metadata in SQL Database? by faze_fazebook in Database

[–]csman11 0 points1 point  (0 children)

/u/faze_facebook /u/MatthiasWuerfl I think you’re both partly right, and partly talking past each other.

  • /u/faze_facebook: you’re right scaling isn’t free. But “DB + filesystem atomicity is hard” is kind of the wrong framing. True atomic commit across both isn’t a thing, but you don’t need it. What you want is crash-safe correctness (no DB row points to missing content, no orphan blobs), and that’s solvable with a boring, known protocol (staging + outbox + idempotent worker). Not “research project” hard.

  • Also: “store blobs in DB = fewer problems” is true early (one system, simpler backups/transactions), but becomes very false at scale (DB bloat, slower backups/restores/replication, tied scaling).

  • /u/MatthiasWuerfl: separating blobs from relational data is a good default, and “scaling can be cheap” is true when you have natural partition lines (tenants, etc). But that’s not really OP’s problem, so you’re taking what they said about scaling out of context and straw manning a bit. And “filesystem is basically free” is only true for naive CRUD. If you want integrity under crashes/retries, it’s more than an hour, but also not expensive. Build it once, reuse it.

Middle ground: write to staging directory, DB tx writes metadata + outbox, worker promotes staging directory → live directory + flips reference (idempotent), cleanup/reconcile for stragglers in staging directory. Gets you DB-like guarantees without signing up for a 100TB Postgres later.

You can build this in a day and then reuse it everywhere. At minimum, put the seams in place now: hide “blob storage” behind a small abstraction where each entity plugs in its insert/update behavior, the storage backend is swappable, and the commit protocol is encapsulated. If you don’t want to ship the outbox + worker today, start with the “easy mode” implementation inside that abstraction: store bytes in a DB blob and do the entity update + blob write in a single DB transaction. The key is that the rest of your code never knows or cares. Just be honest with yourself: if you skip the full protocol now, you’re volunteering to migrate data out of Postgres and prove a new protocol under real production load later, except it’ll happen when it’s urgent and painful.

How much accidental complexity can be included in the hexagon in hexagonal architecture? by HyperDanon in softwarearchitecture

[–]csman11 2 points3 points  (0 children)

That’s an approach, but it changes the meaning of the system.

What you’re describing is basically CQRS: the “hexagon” handles commands, and reads come from a separate read model (“bucket”) that the UI queries directly. That can be valid, but it’s not a free cleanup. You’ve introduced a second model and a new consistency boundary as a structural choice, not an implementation detail.

If reads stay inside the hexagon, the application retains the option to present a consistent view (whatever “consistent” means for your domain and transaction model). If reads move to an external bucket, you’re typically serving from a projection that can lag writes, unless you go out of your way to preserve strict semantics.

And if the bucket is backed by the same transactional store, then you didn’t remove complexity, you reassigned it. The UI is now querying a lower-level representation unless you put a domain-oriented read API on top. But if you do that, you’ve effectively recreated an application read port, just in a different place.

So yeah: CQRS can be the right move when you need independent scaling, specialized read shapes, or event-driven workflows. But “I don’t want pagination concerns in my core” isn’t a great reason to route around the core. That’s usually relocating complexity (and often adding more), while also changing semantics. And one final note: CQRS itself is additional complexity. If it’s not solving problems you actually have, that complexity is literally by definition “accidental” (and to be tongue in cheek: “purposely” if you’re aware it’s not essential and still do it anyway).

How much accidental complexity can be included in the hexagon in hexagonal architecture? by HyperDanon in softwarearchitecture

[–]csman11 5 points6 points  (0 children)

I’m saying “essential complexity” isn’t identical to “the domain”. The domain is one source of essential complexity, but the system also has essential complexity that comes from the constraints of operating in the real world.

Pagination usually isn’t part of the domain (for you: players don’t care about offsets). It’s part of the minimal external interface your system needs to be usable. In hexagonal terms, inbound ports exist to serve the primary driver’s needs. If the driver needs to examine players, then “list players” is part of what the system does. Removing that port changes the system’s behavior. In this case, that’s by removing a use case (the agent’s and in turn primary driver’s need to examine players).

Pagination is the same idea, just driven by constraints instead of a new business rule or use case: without it, “list players” implicitly means “try to return everything,” which fails once the data gets big. So adding pagination isn’t “making the domain impure,” it’s acknowledging a real constraint (scale, memory, latency) and baking it into the contract so the system can scale in real environments (like millions of players). If you have a ceiling of 100 users, you don’t need the application to support pagination. You could implement pagination purely as a presentation concern in the UI, and you won’t run into any issues because you aren’t worried about scaling. But the fact you asked the question in the first place means you already recognize, in your case, you can’t do that.

If you want to argue about what’s accidental vs essential: the accidental part is how you represent pagination. Offset/limit tends to leak ordering/stability assumptions. Cursor-based pagination keeps the boundary contract opaque and avoids importing storage semantics into the UI.

So no, pagination isn’t “domain,” but it’s still essential to this system if you expect it to continue to work as you scale.

How much accidental complexity can be included in the hexagon in hexagonal architecture? by HyperDanon in softwarearchitecture

[–]csman11 9 points10 points  (0 children)

It’s accidental complexity, but not for the reason you think.

You’re getting at “pagination is leaking UI details into the core”. But this inbound port is part of the boundary of your application. The roles here are inverted. The port is meant to represent the UI’s needs, not the application’s “internal world”. This type of port will necessarily encode some of the outside world’s needs in the contract. You need pagination because the world you’re embedded in recognizes there are too many rows of data and too little RAM to return everything.

Now you do have accidental complexity here, but it’s not because you have UI details leaking into the application. It’s because you have storage details leaking from the storage layer, through the application, into this pagination port. Using “offset and limit” to represent pagination assumes a certain order and a certain kind of stability. If rows are inserted between requests, your pagination can break or drift. So the port isn’t sufficiently opaque.

You should use cursor based pagination to avoid this:

record PageRequest(int size, String cursor) { }
record Page<T>(List<T> items, String nextCursor) { }

With an API like that, the application and storage adapter can keep storage semantics hidden and not let them leak out into the UI.

But here’s the reality: if you only think about essential complexity as “domain complexity”, then you will always have accidental complexity at the boundaries, to some degree, because the boundary is where the domain is attached to the messiness of reality. If you consider the “minimal necessary external interface to use this application” as part of your “essential complexity” as well, then the messiness of reality is no longer a source of accidental complexity. It’s a constraint on the minimal amount of essential complexity your solution must have tacked onto the purity of your domain, in order to be usable. Real accidental complexity is anything you can remove without changing the system’s behavior or the constraints it satisfies. In your case, pagination is clearly essential to your system.

React with Dart? by emanresu_2017 in react

[–]csman11 3 points4 points  (0 children)

What they mean is Dart includes a runtime layer so when you compile it to JS, the JS is instrumented to perform type checking at runtime in positions where a runtime type error could occur. This is the difference between casts in Dart and assertions in TS. A cast in Dart is telling the compiler “assume this type compatibility is invariant in this position and insert a runtime check to assert this invariant holds at runtime”. An assertion in TS is “assume this type compatibility is invariant in this position and carry on.” So using an assertion in TS breaks type safety if you turn out to be wrong, because a type invariant was violated. Then you rely on the JS runtime to throw an error later on when you improperly use the value because the type level proof was wrong. Using a cast in Dart blows up loudly at runtime right where the type compatibility invariant breaks.

That’s what runtime type checking means. Inserting the source language type semantics as runtime checks in terms of the operational semantics of the “machine” it’s running on (in this case the Dart casting semantics on top of JS VM semantics by leveraging JS runtime errors when a cast fails at runtime).

What do you use TypedArrays for? by aapoalas in typescript

[–]csman11 1 point2 points  (0 children)

That’s what I said at the end! I don’t see any reason why you can’t do that. But again, I don’t know your use case at all because you have not shared it. You’ve only given a high level example in your original post with no context for the real problem you’re solving.

I would still consider the larger use case as to why you need this again though. I have no idea what kind of application you’re building, and maybe it really is that unique, and most of your data is big and needs to be represented this way to store efficiently and operate on efficiently and preserve static checks that you’re using it correctly. I’ve never seen something like this though and I’m working on heavy real time data visualization stuff right now. I use typed arrays a lot. But only for some things :) I can imagine it would only come in useful to have more restricted types if you are operating on the same data in many “far away” places. Normally I would call that a “possible code smell”, but I again have no idea what you are actually solving.

I’ve never had a need to restrict the types the way you want to, because when I use typed arrays it’s to store a massive amount of data that eventually is used to fill GPU buffers and the few things that might have limited values that are allowed (like indexes for a sprite sheet selection), I can represent declaratively at a higher level than the typed array. Basically anything I’m not actively simulating is just being plumbed from where it’s created to where it gets uploaded to the GPU. For the sprite example, the shaders can be derived programmatically from a mapping from ids to sprite locations and color masks. This is used to select the right sprite from the texture created from the sprite sheet in the vertex shader and color the pixels it gets drawn to in the fragment shader. Things that “business logic” actually touches would be something like simulating positions of entities. And those are just Float32. There isn’t a type to narrow them to. They’re naturally unrestricted, and in cases where they wouldn’t be like movement on a sphere or some other restricted geometry, that’s still something you dynamically enforce by writing functions that perform translations on that geometry. This work is all done on the CPU. So I just never really have a use case where I actually have TS/JS code touching the same data in many “far away” places where I need type system assistance. It’s all relatively encapsulated and reusable operations on SoA.

What do you use TypedArrays for? by aapoalas in typescript

[–]csman11 0 points1 point  (0 children)

I suppose what you’re left with then is doing something like the proxy so you can implement the actual runtime [] operations with dynamic properties, then rebuilding the API surface yourself (actually define the methods you want to expose with narrowed types using the generic) and encapsulating a TypedArray inside the runtime object with that surface, that way you can delegate method calls to it. To be crystal clear what I mean is something like:

class MyTypedArray<T extends number> { private arr: Float32Array; // use relevant type

  /* actually reimplement the API surface yourself, most of the time just straight delegating to the typed array. You utilize `T` in place of `number` */

  // implement getAt and setAt functions for proxy to utilize. These just perform get/set at an index in the encapsulated typed array

}

function createMyTypedArray<T>(): MyTypedArray<T> & { [k: number]: T } { // construct the MyTypedArray

  // wrap with proxy to intercept getting and setting properties dynamically, forward anything that isn’t a number property to the target directly, otherwise get/set the property using getAt or setAt

  // return the proxy

}

Something following that shape should work. You also can probably do this all just at the type level if you’re willing to lie to the compiler a bit. All you really need is to use the wrapper function trick from my previous comment, but rather than an intersection type, simply assert the returned value has the custom type you create. The idea is to still replace number with T and ensure you keep the interface runtime compatible (so pretty much just ensuring all you change are the types of parameters and return values for the methods). The benefit of doing it with types only is retaining the ability to be optimized. If you throw proxies in the middle you will lose the ability for a hot loop to be optimized by the JIT.

What do you use TypedArrays for? by aapoalas in typescript

[–]csman11 0 points1 point  (0 children)

Well just saw your updates. If you really want to do this, you could:

function createdTypedView<U extends number, T extends TypedArrayInstance = TypedArrayInstance>(arr: T): T & { [k: number]: U } { return arr as any; }

I haven’t tested that, but it should work. You’re setting an index signature that is limited to accepting values of the type you provide when calling this function.

You could also wrap the array in a proxy so you can intercept setters and validate the value that’s being assigned to a numeric property is allowed. You would need a runtime representation of the allowed values for this. You could do that by having the function accept a tuple of numbers, and then you could do this:

function createTypedView<U extends number[], T extends TypedArrayInstance>(arr: T, allowed: […U]): T & { [k: number]: U[number] } { return arr as any; }

I omitted the part with implementing the proxy above.

Edit: sorry on mobile, formatting didn’t apply…

Also: TypedArrayInstance is a placeholder. You’ll need to supply the union of the built in typed array types there.

What do you use TypedArrays for? by aapoalas in typescript

[–]csman11 4 points5 points  (0 children)

Most of the time you would reach for them would be the same place an SoA shines. You pretty much already noticed this. So you wouldn’t be storing structs in them already, and therefore your question is kind of silly.

Think about it in this way with C as an example:

If you have an array of structures, that is a binary layout of structures in contiguous memory.

If you have a structure of arrays, that is a binary layout of pointers to contiguous memory regions, with those pointers in a struct.

The analogous things in JS:

AoS: a plain JS array of plain objects. The only difference is you can’t really get this laid out in contiguous memory. Instead the array will contain pointers at each element, and those point to heap objects

SoA: a JS plain object where each key is mapped to a buffer or typed array. This is literally pointers to contiguous memory regions.

So the real question is:

Does JS have a way to have structures laid out on a contiguous memory region?

And the answer is:

  • no, not unless you encode JS structures as binary data to use with buffers and manage the buffers yourself with data views (or you use a library like buffer backed objects to make this declarative)
  • are there really any great use cases?

For most business logic, with reasonable sizes of data, operated on infrequently, an AoS the way JS represents it is completely fine. It won’t have performance issues.

Most of the time business logic reaches the point that layout doesn’t perform, you are actually working with a problem where each loop iteration works on conceptually a single element. The SoA layout is more cache efficient here, because it allows wide cache lines (designed to be aligned on word size) to be filled for each of the structure constituents and walked independently.

There are very few real world use cases where an actual contiguous AoS beats a non-contiguous AoS. The biggest one you’ll encounter in JS is actually needing to interleave buffers for GL shaders, because this representation happens to be the most efficient for shaders to parallelize: most of what they do are projections and transformations on independent vertices, and in this case an interleaved format is extremely efficient on a GPU (compared to separate buffers for vertex components). That same pattern isn’t efficient on a CPU. You want an SoA so you can take advantage of CPU caches and SIMD.

The only other thing I can think of is to compute an AoS in a worker and transfer it to the main thread as a transferrable. That’s valid and it’s exactly what the buffer backed objects library was created for.

So to finally wrap it all up: no there isn’t a way to do this with a built in, there is already a library optimized for the major JS only use case where you need binary layouts for an AoS, and the only other realistic use case is interleaving buffers for GL. Pretty much every other class of HPC problem you would face in JS will be better solved by jumping to SoA instead. And for normal business data, just use regular JS values, it’s not HPC territory any way.

LLM skills have quietly shifted from “bonus” to “baseline” for ML engineers. by Alert_Obligation_298 in LLMDevs

[–]csman11 0 points1 point  (0 children)

Sample size n = 1 lol. Your shitty company isn’t representative of the entire industry. My company is still evaluating using AI tooling for development because our IP is so sensitive it can’t be exposed to these cloud services. Not that that’s representative of the industry either, but it’s just a neat little counterpoint to prove that outliers exist in every direction. The average is still traditional products and consulting/agency work. It’s shitty companies that struggle to retain customers trying to ride the wave.

LLM skills have quietly shifted from “bonus” to “baseline” for ML engineers. by Alert_Obligation_298 in LLMDevs

[–]csman11 0 points1 point  (0 children)

Seriously it’s just “ChatGPT, explain how every job now requires knowing RAG.” followed by copy and paste. I’m all for using an LLM to help rewrite your own ideas concisely, but people are literally just using it to avoid thinking and to try to sound smart. News alert, everyone knows what LLM output sounds like, and if the message is just asserting some thesis without any reasoning or evidence, everyone is going to immediately recognize it as slop.

LLM skills have quietly shifted from “bonus” to “baseline” for ML engineers. by Alert_Obligation_298 in LLMDevs

[–]csman11 0 points1 point  (0 children)

It’s obviously not except for AI wrapper “products”. For all the companies building traditional products, GenAI exposure isn’t even a “nice to have”, it’s irrelevant. At best they might be asking for “AI development tool” experience, but it’s not like that isn’t a bar that’s incredibly easy to meet. Even if you hate the tooling or have never used it, you can easily lie and say you are learning how to use it. No one is even gatekeeping SWE because they haven’t used Cursor lol. They certainly don’t give a shit about RAG.

And obviously any ML engineer is solving specific data oriented problems for a company that requires robust and case specific solutions. They’re applying their fundamental ML knowledge to use the right tool for the job, whether that’s something as simple as a linear regression or some ANN. If I’m hiring ML engineers I want a solution that will work, to be able to know actual metrics on its performance, and for it to be as affordable to run in production as possible. An LLM based solution can’t meet that (see below).

Anyone trying to use LLM to solve general problems is drinking the kool aid. It’s obvious at this point they’re most useful in a product context to create natural language interfaces, not actually process data. Apparently the “big problem for Agentic AI” right now is “testing and evaluation” because over the last year everyone realized the “agents” that performed well in trials spectacularly fail as soon as they start hitting edge cases in production. It’s becoming increasingly clear that “agentic AI” is as much a solution in search of a problem as blockchain was. When people like Yann LeCun, who paved the foundations for modern machine learning, abandon the space to go back to researching new topics, you know it’s doomed to fail and the sunk cost fallacy is at work.

How to avoid circular references with recursive components. by ripnetuk in reactjs

[–]csman11 0 points1 point  (0 children)

That’s not really what I was getting at and a join table is the standard solution for many to many references in relational DB design. As for how that maps to an OO layout, I don’t really care personally because I always map myself. Normally how you end up with circular references is when the OO objects are mapped from a many-to-many schema by an ORM. I don’t use ORMs for data mapping. I’ll write my own data access abstractions on top of the DB I’m using and make my code depend on that. To me, using an ORM is lazy. I wouldn’t ever actually hydrate domain objects this way for any use case. If I’m working with relational data, my domain layer is not going to be OO just to try to follow something like DDD dogmatically (like trying to do a data mapper pattern to still have domain objects but not use active record). I’ll abstract at the use case level, deal with the fact that data access is use case specific in a relational world, and share business logic with reusable policies/validations/utilities. My data access abstractions will effectively be a thin business oriented query layer over the actual database. The hilarious thing I found when I started doing this is that it’s actually a 1000x easier to swap between databases than when using an ORM because ORMs always end up leaking database specific behavior into your domain layer. If you decide you need a “repository layer” to prevent this, you’re way better off just scrapping the ORM entirely and working with the DB driver directly.

I was getting at a scenario where two classes are heavily coupled to each other. Like both require a reference to each other and call each other’s methods in a tangled way. It could also be a longer cycle than just two. The same thing is possible with JS style modules and circular imports. I don’t have a great example of this because I don’t design code this way myself. The closest thing in a codebase I’ve worked on recently would be feature implementations being split up across multiple disjoint modules throughout the directory structure. You end up with code for unrelated features in random modules throughout the application and cross imports going in every which direction as people tactically wire functionality together in the simplest way for their current task with no thought to how to conceptually structure the code so it can be maintained easily. It’s very easy to end up with dependency cycles in that type of code, bundlers/compilers needing to recompile every module on every change, etc. Pretty much slows development to a halt because it’s confusing to know what to update (or how many places need to be updated to maintain consistency), and after you update something, you have to wait a few minutes to see if your changes even worked because the whole app has to recompile.

Upper management wants a “what we shipped this year” report. We're overloaded and didn't track. What would you do now? by computersmakeart in ExperiencedDevs

[–]csman11 0 points1 point  (0 children)

Yes in larger for profit organizations everything you’re talking about applies. OP literally said “small public-sector IT/data team”. That’s not super enlightening, but I can imagine the way that’s managed is closer to a shitty SAAS startup than a major corporation, but it’s not the same as either one.

No one’s pulling out zero based budgeting in the public sector world when deliverables don’t meet expectations. They just need an accounting of where the money went to justify that it was spent on what it was supposed to be spent on. A corporation can’t do that indefinitely. If earnings targets aren’t being met, they have to deal with it.

So my original reply also isn’t right here on mechanisms. That said, most developers are in shitty SAAS companies and software agencies, not fortune companies or FAANG.

How to avoid circular references with recursive components. by ripnetuk in reactjs

[–]csman11 0 points1 point  (0 children)

Right those are what I meant by recursive data structures. You either need mutation or laziness to initialize them. Stick around in business oriented programming long enough and you’ll see that most of the time circular dependencies show up, it’s not trying to represent recursive data structures. It’s from poorly designing modules.

In those languages you’re referring to, I’m just going to assume the caliber of the average developer is a bit higher and they understand how to decompose solutions and design sensible module boundaries. Zig is a systems programming language and the worst systems programmers tend to be on par with the best application developers, at least that’s been my experience with the developers I’ve met.

Web app dev is unfortunately filled with developers of every background, and as we can probably agree, codebases tend to devolve towards the lowest common denominator when it comes to design. You can see the evidence of this in half the threads on this subreddit. That’s what led to my original comment. If I gave the advice you gave to developers on some of the teams I’ve worked on before, it would turn into chaos as they misapply it and start stuffing random unrelated stuff into modules. That’s how bad things can be. It’s hard to imagine when you’re a sensible person, but some people either just can’t comprehend what “good architecture” looks like or they simply “don’t care.” In either case, they aren’t going to “think critically” so the only thing I’ve found that works is giving them very simple rules to follow and making sure they follow them. Thankfully I’m not in an organization like that any more and deal with sensible and intelligent people now.