frontpage.
newsnewestaskshowjobs

Made with ♥ by @iamnishanth

Open Source @Github

fp.

Ice gRPC toolkit and Protobuf alternative

https://github.com/zeroc-ice/ice
1•just_human•46s ago•0 comments

Show HN: What to Do with an Old iPad

http://odb.ar/blog/2025/09/05/hosting-my-blog-on-an-iPad-2.html
1•owenmakes•1m ago•0 comments

Rails World 2025 Opening Keynote – David Heinemeier Hansson [video]

https://www.youtube.com/watch?v=gcwzWzC7gUA
1•foofoo4u•5m ago•0 comments

Show HN: We wrote an open-source Text to CAD app

https://github.com/Adam-CAD/CADAM
1•zachdive•5m ago•0 comments

Apple Arcade, Six Years IN

https://sixcolors.com/link/2025/09/apple-arcade-six-years-in/
1•ksec•6m ago•1 comments

Ask HN: What are the best biographies of the firm?

1•miletus•6m ago•0 comments

SQLite can handle most of it

https://binaryigor.com/sqlite-db-simple-in-process-reliable-fast.html
1•BinaryIgor•8m ago•0 comments

Value type and bits pattern in MoonBit, 30% faster than Rust

https://www.moonbitlang.com/blog/moonbit-value-type
1•dlib•8m ago•0 comments

Understanding Bazel Remote Caching

https://blogsystem5.substack.com/p/bazel-remote-caching
2•oftenwrong•10m ago•0 comments

Just in Time for the Most Overengineered Calculator (2023)

https://l-m.dev/cs/jitcalc/
1•hggh•10m ago•0 comments

GPT-5 negotiates harder and better than Opus 4.1

https://mdahardy.substack.com/p/gpt-5-negotiates-harder-and-better
1•mdahardy•11m ago•0 comments

Why Language Models Hallucinate

https://openai.com/index/why-language-models-hallucinate
1•meetpateltech•13m ago•0 comments

If AI agents take the jobs, who buys the stuff?

2•babua•14m ago•0 comments

PKM apps need to get better at resurfacing information

https://ankursethi.com/blog/pkm-apps-need-to-get-better-at-resurfacing-information/
1•abhin4v•14m ago•0 comments

Freeway guardrails are now a favorite target of thieves

https://laist.com/news/transportation/guardrails-aluminum-theft
3•jaredwiener•17m ago•0 comments

Elon Musk Could Become First Trillionaire Under New Tesla Pay Plan

https://www.nytimes.com/2025/09/05/business/elon-musk-tesla-pay-trillionaire.html
3•dctoedt•18m ago•0 comments

Cagent – customizable multi-agent runtime

https://github.com/docker/cagent
1•fossa1•18m ago•0 comments

Why Everybody Is Losing Money on AI

https://www.wheresyoured.at/why-everybody-is-losing-money-on-ai/
11•speckx•19m ago•0 comments

We No Longer Lock Premium Features

https://neon.com/blog/why-we-no-longer-lock-premium-features
1•superchink•19m ago•0 comments

Show HN: BenchWrk – Get Logs in VSCode

https://marketplace.visualstudio.com/items?itemName=Benchwrk.benchwrk
1•aliatwa•21m ago•0 comments

Show HN: I want to create a TradingView alternative, can you try it?

https://www.aulico.com
1•feedbackcolle•21m ago•0 comments

The Recursive Loop: How Code Shapes Minds – Kenneth Reitz

https://kennethreitz.org/essays/2025-09-05-the_recursive_loop_how_code_shapes_minds
1•rbanffy•21m ago•0 comments

1999 D&D RPG Planescape: Torment is getting an unofficial DLC

https://www.pcgamer.com/games/rpg/one-of-the-best-rpgs-of-all-time-is-getting-its-first-ever-unof...
1•calimoro78•21m ago•1 comments

First commercial carbon storage facility begins operations under North Sea

https://www.cbsnews.com/news/world-first-commercial-carbon-capture-storage-facility-norway-opens/
2•gmays•22m ago•0 comments

European Commission fines Google €2.95B over abusive ad tech practices

https://ec.europa.eu/commission/presscorner/detail/en/ip_25_1992
3•ChrisArchitect•23m ago•0 comments

The danger in this permacrisis is that that nothing changes

https://www.theguardian.com/commentisfree/2025/aug/04/real-danger-permacrisis-political-drama-ris...
2•PaulHoule•23m ago•0 comments

Synthesia's AI clones are more expressive

https://www.technologyreview.com/2025/09/04/1123054/synthesias-ai-clones-are-more-expressive-than...
1•Brajeshwar•24m ago•0 comments

I've built a website just by prompting AI

https://aumont.fr/posts/A-web-site-with-AI/
1•Torpenn•25m ago•0 comments

Geoffrey Hinton: "AI will make a few people much richer and most people poorer"

https://www.ft.com/content/31feb335-4945-475e-baaa-3b880d9cf8ce
4•cs702•26m ago•2 comments

Designing audio-first reading experience

https://usekatalog.com/blog/designing-audio-first-reading-experience/
1•fmerian•26m ago•0 comments
Open in hackernews

Protobuffers Are Wrong

https://reasonablypolymorphic.com/blog/protos-are-wrong/
82•b-man•1h ago

Comments

taeric•1h ago
I'm more than a little curious what event caused such a strong objection to protobuffers. :D

I do tend to agree that they are bad. I also agree that people put a little too much credence in "came from Google." I can't bring myself to have this much anger towards it. Had to have been something that sparked this.

mrits•1h ago
I've used them almost daily for 15 years. They are way down the list of things I'd want improved. It has been interesting to see the protobuffers killers die out every few years though
rimunroe•1h ago
I'm just a frontend developer so most of my exposure is just as an API consumer and not someone working on the service side of things. That said:

A few years ago I moved to a large company where protobufs were the standard way APIs were defined. When I first started working with the generated TypeScript code, I was confused as to why almost all fields on generated object types were marked as optional. I assumed it was due to the way people were choosing to define the API at first, but then I learned this was an intentional design choice on the part of protobufs.

We ended up having to write our own code to parse the responses from the "helpfully" generated TypeScript client's responses. This meant we had to also handle rejecting nonsensical responses where an actually required field wasn't present, which is exactly the sort of thing I'd want generated clients to do. I would expect having to do some transformation myself, but not to that degree. The generated client was essentially useless to us, and the protocol's looseness offered no discernible benefit over any other API format I've used.

I imagine some of my other complaints could be solved with better codegen tools, but I think fundamentally the looseness of the type system is a fatal issue for me.

thinkharderdev•54m ago
Yeah, as soon as you have a moderately complex type the generated code is basically useless. Honestly, ~80% of my gripes about protocol buffers could be alleviated by just allowing me to mark a message field as required.
iamdelirium•48m ago
You think you do but you really don't.

What happens if you mark a field as required and then you need to delete it in the future? You can't because if someone stored that proto somewhere and is no longer seeing the field, you just broke their code.

ozgrakkurt•29m ago
Maybe you don’t delete it then?
taeric•21m ago
I mean, this is essentially the same lesson that database admins learn with nullable fields. Often it isn't the "deleting one is hard" so much as "adding one can be costly."

It isn't that you can't do it. But the code side of the equation is the cheap side.

thinkharderdev•22m ago
If you need to deserialize an old version then it's not a problem. The unknown field is just ignored during deserialization. The problem is adding a required field since some clients might be sending the old value during the rollout.

But in some situations you can be pretty confident that a field will be required always. And if you turn out to be wrong then it's not a huge deal. You add the new field as optional first (with all upgraded clients setting the value) and then once that is rolled out you make it required.

And if a field is in fact semantically required (like the API cannot process a request without the data in a field) then making it optional at the interface level doesn't really solve anything. The message will get deserialized but if the field is not set it's just an immediate error which doesn't seem much worse to me than a deserialization error.

taeric•34m ago
To add to the sibling, I've seen this with Java enums a lot. People will add it so that the value is consumed using the enum as fast as they can. This works well as long as the value is not retrieved from data. As soon as you do that, you lose the ability to add to the possible values in a rolling release way. It can be very frustrating to know that we can't push a new producer of a value before we first change all consumers. Even if all consumers already use switch statements with default clauses to exhaustively cover behavior.
thinkharderdev•2m ago
But this is something you should be able to handle on a case-by-case basis. If you have a type which is stored durably as protobuf then adding required fields is much harder. But if you are just dealing with transient rpc messages then it can be done relatively easily in a two step process. First you add the field as optional and then once all producers are upgraded (and setting the new field), make it required. It's annoying for sure but still seems better than having everything optional always and needing to deal with that in application code everywhere.
thinkharderdev•59m ago
I feel like I could have written an article like this at various points. Probably while spending two hours trying to figure out a way to represent some protobuf type in a sane way internally.
mike_hearn•51m ago
He says that in the article; he had to work on a "compiler" project that was much harder than it should have been because of protobuf's design choices.
taeric•34m ago
Yeah, I saw that. I took that as something that happened in the past, though. Certainly colored a lot of the thinking, but feels like something more immediate had to have happened. :D
ndr•1h ago
Not even before the first line ends you get "They’re clearly written by amateurs".

This is a rage bait, not worth the read.

jilles•1h ago
The best way to get your point across is by starting with ad-hominem attacks to assert your superior intelligence.
notmyjob•52m ago
I disagree, unless you are in the majority.
perching_aix•20m ago
Is this in reference to the blogpost, the comment above, or your own comment? Cause it honestly works for all of them.
jeffbee•1h ago
Yep, the article opens with a Hall of Fame-grade compound fallacy: a strawman refutation of a hypothetical ad hominem that nobody has argued.

You can kinda see how this author got bounced out of several major tech firms in one year or less, each, according to their linkedin.

omnicognate•54m ago
It's a terrible attitude and I agree that sort of thing shouldn't be (and generally isn't) tolerated for long in a professional environment.

That said the article is full of technical detail and voices several serious shortcomings of protobuf that I've encountered myself, along with suggestions as to how it could be done better. It's a shame it comes packaged with unwarranted personal attacks.

BugsJustFindMe•1h ago
If only the article offered both detailed analyses of the problems and also solutions. Wait, it does! You should try reading it.
IncreasePosts•1h ago
It's written by amateurs, but solves problems that only Google(one of the biggest/most advanced tech companies in the world) has.
bbkane•1h ago
The author makes good arguments; I wish they'd offered some alternatives.

Despite issues, protobufs solve real problems and (imo) bring more value than cost to a project. In particular, I'd much rather work with protobufs and their generated ser/de than untyped json

jeffbee•1h ago
Type system fans are so irritating. The author doesn't engage with the point of protocol buffers, which is that they are thin adapters between the union of things that common languages can represent with their type systems and a reasonably efficient marshaling scheme that can be compact on the wire.
dano•1h ago
It is a 7 year old article without specifying alternatives to an "already solved problem."

So HN, what are the best alternatives available today and why?

gsliepen•1h ago
Something like MessagePack or CBOR, and if you want versioning, just have a version field at the start. You don't require a schema to pack/unpack, which I personally think is a good thing.
mgaunard•56m ago
Arrow is also becoming a good contender, with the extra benefit it is better optimized for data batches.
fmbb•55m ago
> You don't require a schema to pack/unpack

Then it hardly solves the same problem Protobuf solves.

rapsey•1h ago
There are none, protobufs are great.
nicce•48m ago
Depends. ASN.1 is a beast and another industry standard, but unfortunately the best tooling is closed source.
thinkharderdev•48m ago
Support across languages etc is much less mature but I find thrift serialization format to be much nicer than protobuf. The codegen somehow manages to produce types that look like types I would actually write compared to the monstrosities that protoc generates.
lalaithion•1h ago
Protocol buffers suck but so does everything else. Name another serialization declaration format that both (a) defines which changes can be make backwards-compatibly, and (b) has a linter that enforces backwards compatible changes.

Just with those two criteria you’re down to, like, six formats at most, of which Protocol Buffers is the most widely used.

And I know the article says no one uses the backwards compatible stuff but that’s bizarre to me – setting up N clients and a server that use protocol buffers to communicate and then being able to add fields to the schema and then deploy the servers and clients in any order is way nicer than it is with some other formats that force you to babysit deployment order.

The reason why protos suck is because remote procedure calls suck, and protos expose that suckage instead of trying to hide it until you trip on it. I hope the people working on protos, and other alternatives, continue to improve them, but they’re not worse than not using them today.

jitl•1h ago
Not widely used but I like Typical's approach

https://github.com/stepchowfun/typical

> Typical offers a new solution ("asymmetric" fields) to the classic problem of how to safely add or remove fields in record types without breaking compatibility. The concept of asymmetric fields also solves the dual problem of how to preserve compatibility when adding or removing cases in sum types.

cornstalks•51m ago
I've never heard of Typical but the fact they didn't repeat protobuf's sin regarding varint encoding (or use leb128 encoding...) makes me very interested! Thank you for sharing, I'm going to have to give it a spin.
zigzag312•26m ago
It looks similar to how vint64 lib encodes varints. Total length of varint can be determined via the first byte alone.
zigzag312•25m ago
This actually looks quite interesting.
noitpmeder•1h ago
Not that I love it -- but SBE (Simple Binary Encoding) is a _decent_ solution in the realm of backwards/forwards compatibility.
jnwatson•1h ago
ASN.1 implements message versioning in an extremely precise way. Implementing a linter would be trivial.
mattnewton•1h ago
Exactly, I think of protobuffers like I think of Java or Go - at least they weren’t writing it in C++.

Dragging your org away from using poorly specified json is often worth these papercuts IMO.

mgaunard•58m ago
in the systems I built I didn't bother with backwards compatibility.

If you make any change, it's a new message type.

For compatibility you can coerce the new message to the old message and dual-publish.

o11c•52m ago
I prefer a little builtin backwards (and forwards!) compatibility (by always enforcing a length for each object, to be zero-padded or truncated as needed), but yes "don't fear adding new types" is an important lesson.
tomrod•54m ago
> Name another serialization declaration format that both (a) defines which changes can be make backwards-compatibly, and (b) has a linter that enforces backwards compatible changes.

ASCII text (tongue in cheek here)

tyleo•49m ago
We use protocol buffers on a game and we use the back compat stuff all the time.

We include a version number with each release of the game. If we change a proto we add new fields and deprecate old ones and increment the version. We use the version number to run a series of steps on each proto to upgrade old fields to new ones.

swiftcoder•7m ago
> We use the version number to run a series of steps on each proto to upgrade old fields to new ones

It sounds like you've built your own back-compat functionality on top of protobuf?

The only functionality protobuf is giving you here is optional-by-default (and mandatory version numbers, but most wire formats require that)

maximilianburke•34m ago
Flatbuffers satisfies those requirements and doesn’t have varint shenanigans.
Analemma_•1h ago
The "no enums as map keys" thing enrages me constantly. Every protobuf project I've ever worked with either has stringly-typed maps all over the place because of this, or has to write its own function to parse Map<String, V> into Map<K, V> from the enums and then remember to call that right after deserialization, completely defeating the purpose of autogenerated types and deserializers. Why does Google put up with this? Surely it's the same inside their codebase.
riku_iki•1h ago
And v1 and v2 protos didn't even have maps.

Also, why you use string as a key and not int?

Arainach•1h ago
proto2 absolutely supported the map type.
riku_iki•1h ago
It could be, it looks like there was some versions misalignment:

The maps syntax is only supported starting from v3.0.0. The "proto2" in the doc is referring to the syntax version, not protobuf release version. v3.0.0 supports both proto2 syntax and proto3 syntax while v2.6.1 only supports proto2 syntax. For all users, it's recommended to use v3.0.0-beta-1 instead of v2.6.1. https://stackoverflow.com/questions/50241452/using-maps-in-p...

Arainach•1h ago
Maps are not a good fit for a wire protocol in my experience. Different languages often have different quirks around them, and they're non-trivial to represent in a type-safe way.

If a Map is truly necessary I find it better to just send a repeated Message { Key K, Value V } and then convert that to a map in the receiving end.

dweis•30m ago
I believe that the reason for this limitation is that not all languages can represent open enums cleanly to gracefully handle unknown enums upon schema skew.
mountainriver•1h ago
> Protobuffers correspond to the data you want to send over the wire, which is often related but not identical to the actual data the application would like to work with

This sums up a lot of the issues I’ve seen with protobuf as well. It’s not an expressive enough language to be the core data model, yet people use it that way.

In general, if you don’t have extreme network needs, then protobuf seems to cause more harm than good. I’ve watched Go teams spend months of time implementing proto based systems with little to no gain over just REST.

nicce•50m ago
On the other hand, ASN.1 is very expressive and can cover pretty much anything, but Protobuff was created because people thought ASN.1 is too complex. I guess we can't have both.
recursive•33m ago
Protobuf is independent from REST. You can have either one. Or both. Or neither. One has nothing to do with the other.
jt2190•1h ago
(2018)
BugsJustFindMe•1h ago
I went into this article expecting to agree with part of it. I came away agreeing with all of it. And I want to point out that Go also shares some of these catastrophic data decisions (automatic struct zero values that silently do the wrong thing by default).
xmddmx•1h ago
I share the author's sentiment. I hate these things.

True story: trying to reverse engineer macOS Photos.app sqlite database format to extract human-readable location data from an image.

I eventually figured it out, but it was:

A base64 encoded Binary Plist format with one field containing a ProtoBuffer which contained another protobuffer which contained a unicode string which contained improperly encoded data (for example, U+2013 EN DASH was encoded as \342\200\223)

This could have been a simple JSON string.

fluoridation•40m ago
I mean... you can nest-encode stuff in any serial format. You're not describing a problem either intrinsic or unique to Protobuf, you're just seeing the development org chart manifested into a data structure.
xmddmx•4m ago
Good points this wasn't entirely a protobuf-specific issue, so much as it was a (likely hierarchical and historical set of) bad decisions to use it at all.

Using Protobuffers for a few KB of metadata, when the photo library otherwise is taking multiple GB of data, is just pennywise pound foolish.

Of course, even my preference for a simple JSON string would be problematic: data in a database really should be stored properly normalized to a separate table and fields.

My guess is that protobuffers did play a role here in causing this poor design. I imagine this scenario:

- Photos.app wants to look up location data

- the server returns structured data in a ProtoBuffer

- there's no easy or reasonable way to map a protobuf to database fields (one point of TFA)

- Surrender! just store the binary blob in SQLITE and let the next poor sod deal with it

seanw444•20m ago
That's horrendous. For some reason I imagine Apple's software to be much cleaner, but I guess that's just the marketing getting to my head. Under the hood it's still the same spaghetti.
mkl95•1h ago
If you mostly write software with Go you'll likely enjoy working with protocol buffers. If you use the Python or Ruby wrappers you'd wish you had picked another tech.
jonathrg•54m ago
The generated types in go are horrible to work with. You can't store instances of them anywhere, or pass them by value, because they contain a bunch of state and pointers (including a [0]sync.Mutex just to explicitly prohibit copying). So you have to pass around pointers at all times, making ownership and lifetime much more complicated than it needs to be. A message definition like this

    message AppLogMessage {
        sint32 Value1 = 1;
        double Value2 = 2;
    }
becomes

    type Example struct {
        state                    protoimpl.MessageState 
        xxx_hidden_Value1        int32                  
        xxx_hidden_Value2        float64                  
        xxx_hidden_unknownFields protoimpl.UnknownFields
        sizeCache                protoimpl.SizeCache
    }
For [place of work] where we use protobuf I ended up making a plugin to generate structs that don't do any of the nonsense (essentially automating Option 1 in the article):

    type ExamplePOD struct {
        Value1 int32
        Value2 float64
    }
with converters between the two versions.
vander_elst•1h ago
Always initializing with a default and no algebraic types is an always loaded foot gun. I wonder if the people behind golang took inspiration from this.
wrsh07•1h ago
The simplest way to understand go is that it is a language that integrates some of Google's best cpp features (their lightweight threads and other multi threading primitives are the highlights)

Beyond that it is a very simple language. But yes, 100%, for better and worse, it is deeply inspired by Google's codebase and needs

iamdelirium•1h ago
Yeah, oneOf fields can be repeated but you can just wrap them in a message. It's not as pretty but I've never had any issues with this.

The fact that the author is arguing for making all messages required means they don't understand the reasoning for why all fields are optional. This breaks systems (there are are postmortems outlining this) then there are proto mismatches .

nu11ptr•1h ago
Should have (2018) call out
ants_everywhere•1h ago
> Maintain a separate type that describes the data you actually want, and ensure that the two evolve simultaneously.

I don't actually want to do this, because then you have N + 1 implementations of each data type, where N = number of programming languages touching the data, and + 1 for the proto implementation.

What I personally want to do is use a language-agnostic IDL to describe the types that my programs use. Within Google you can even do things like just store them in the database.

The practical alternative is to use JSON everywhere, possibly with some additional tooling to generate code from a JSON schema. JSON is IMO not as nice to work with. The fact that it's also slower probably doesn't matter to most codebases.

thinkharderdev•29m ago
> I don't actually want to do this, because then you have N + 1 implementations of each data type, where N = number of programming languages touching the data, and + 1 for the proto implementation.

I think this is exactly what you end up with using protobuf. You have an IDL that describes the interface types but then protoc generates language-specific types that are horrible so you end up converting the generated types to some internal type that is easier to use.

Ideally if you have an IDL that is more expressive then the code generator can create more "natural" data structures in the target language. I haven't used it a ton, but when I have used thrift the generated code has been 100x better than what protoc generates. I've been able to actually model my domain in the thrift IDL and end up with types that look like what I would have written by hand so I don't need to create a parallel set of types as a separate domain model.

MountainTheme12•1h ago
I agree with the author that protobuf is bad and I ran into many of the issues mentioned. It's pretty much mandatory to add version fields to do backwards compatibility properly.

Recently, however, I had the displeasure of working with FlatBuffers. It's worse.

wrsh07•59m ago
> This insane list of restrictions is the result of unprincipled design choices and bolting on features after the fact

I'm not very upset that protobuf evolved to be slightly more ergonomic. Bolting on features after you build the prototype is how you improve things.

Unfortunately, they really did design themselves into a corner (not unlike python 2). Again, I can't be too upset. They didn't have the benefit of hindsight or other high performance libraries that we have today.

techbrovanguard•58m ago
i used protobuffers a lot at $previous_job and i agree with the entire article. i feel the author’s pain in my bones. protobuffers are so awful i can’t imagine google associating itself with such an amateur, ad hoc, ill-defined, user hostile, time wasting piece of shit.

the fact that protobuffers wasn’t immediately relegated to the dustbin shows just how low the bar is for serialization formats.

fmbb•58m ago
Well, worse is better.
briandw•58m ago
The crappy system that everyone ends up using is better than the perfectly designed system that's only seen in academic papers. Javascript is the poster-child of Worse is Better. Protobuffs are a PITA, but they are widely used and getting new adoption in industry. https://en.wikipedia.org/wiki/Worse_is_better
pshirshov•56m ago
I've created several IDL compilers addressing all issues of protobuf and others.

This particular one provides strongest backward compatibility guarantees with automatic conversion derivation where possible: https://github.com/7mind/baboon

Protobuf is dated, it's not that hard to make better things.

defraudbah•53m ago
you are absolutely right!

what alternative do we have? sending json and base64 strings

allanrbo•49m ago
Sometimes you are integrating with system that already use proto though. I recently wrote a tiny, dependency-free, practical protobuf (proto3) encoder/decoder. For those situations where you need just a little bit of protobuf in your project, and don't want to bother with the whole proto ecosystem of codegen and deps: https://github.com/allanrbo/pb.py
imtringued•47m ago
>The solution is as follows:

> * Make all fields in a message required. This makes messages product types.

Meanwhile in the capnproto FAQ:

>How do I make a field “required”, like in Protocol Buffers?

>You don’t. You may find this surprising, but the “required” keyword in Protocol Buffers turned out to be a horrible mistake.

I recommend reading the rest of the FAQ [0], but if you are in a hurry: Fixed schema based protocols like protobuffers do not let you remove fields like self describing formats such as JSON. Removing fields or switching them from required to optional is an ABI breaking change. Nobody wants to update all servers and all clients simultaneously. At that point, you would be better off defining a new API endpoint and deprecating the old one.

The capnproto faq article also brings up the fact that validation should be handled on the application level rather than the ABI level.

[0] https://capnproto.org/faq.html

jsnell•45m ago
Discussed many times over the years:

https://news.ycombinator.com/item?id=18188519 (299 comments)

https://news.ycombinator.com/item?id=21871514 (215 comments)

https://news.ycombinator.com/item?id=35281561 (59 comments)

tptacek•33m ago
There are a lot of great comments on these old threads, and I don't think there's a lot of new science in this field since 2018, so the old threads might be a better read than today's.

Here's a fun one:

https://news.ycombinator.com/item?id=21873926

ericpauley•42m ago
I lost the plot here when the author argued that repeated fields should be implemented as in the pure lambda calculus...

Most of the other issues in the article can be solved be wrapping things in more messages. Not great, not terrible.

As with the tightly-coupled issues with Go, I'll keep waiting for a better approach any decade now. In the meantime, both tools (for their glaring imperfections) work well enough, solve real business use cases, and have a massive ecosystem moat that makes them easy to work with.

gethly•35m ago
I too was using PBs a lot, as they are quite popular in the Go world. But i came to the conclusion that they and gRPC are more trouble than they are worth. I switched to JSON, HTTP "REST" and websockets, if i need streaming, and am as happy as i could be.

I get the api interoperability between various languages when one wants to build a client with strict schema but in reality, this is more of a theory than real life.

In essence, anyone who subscribes to YAGNI understands that PB and gRPC are a big no-no.

PS: if you need binary format, just use cbor or msgpack. Otherwise the beauty of json is that it human-readable and easily parseable, so even if you lack access to the original schema, you can still EASILY process the data and UNDERSTAND it as well.

kentonv•34m ago
Previous discussions:

* https://news.ycombinator.com/item?id=18188519

* https://hn.algolia.com/?q=%22Protobuffers+Are+Wrong%22

I guess I'll, once again, copy/paste the comment I made when this was first posted: https://news.ycombinator.com/item?id=18190005

--------

Hello. I didn't invent Protocol Buffers, but I did write version 2 and was responsible for open sourcing it. I believe I am the author of the "manifesto" entitled "required considered harmful" mentioned in the footnote. Note that I mostly haven't touched Protobufs since I left Google in early 2013, but I have created Cap'n Proto since then, which I imagine this guy would criticize in similar ways.

This article appears to be written by a programming language design theorist who, unfortunately, does not understand (or, perhaps, does not value) practical software engineering. Type theory is a lot of fun to think about, but being simple and elegant from a type theory perspective does not necessarily translate to real value in real systems. Protobuf has undoubtedly, empirically proven its real value in real systems, despite its admittedly large number of warts.

The main thing that the author of this article does not seem to understand -- and, indeed, many PL theorists seem to miss -- is that the main challenge in real-world software engineering is not writing code but changing code once it is written and deployed. In general, type systems can be both helpful and harmful when it comes to changing code -- type systems are invaluable for detecting problems introduced by a change, but an overly-rigid type system can be a hindrance if it means common types of changes are difficult to make.

This is especially true when it comes to protocols, because in a distributed system, you cannot update both sides of a protocol simultaneously. I have found that type theorists tend to promote "version negotiation" schemes where the two sides agree on one rigid protocol to follow, but this is extremely painful in practice: you end up needing to maintain parallel code paths, leading to ugly and hard-to-test code. Inevitably, developers are pushed towards hacks in order to avoid protocol changes, which makes things worse.

I don't have time to address all the author's points, so let me choose a few that I think are representative of the misunderstanding.

> Make all fields in a message required. This makes messages product types.

> Promote oneof fields to instead be standalone data types. These are coproduct types.

This seems to miss the point of optional fields. Optional fields are not primarily about nullability but about compatibility. Protobuf's single most important feature is the ability to add new fields over time while maintaining compatibility. This has proven -- in real practice, not in theory -- to be an extremely powerful way to allow protocol evolution. It allows developers to build new features with minimal work.

Real-world practice has also shown that quite often, fields that originally seemed to be "required" turn out to be optional over time, hence the "required considered harmful" manifesto. In practice, you want to declare all fields optional to give yourself maximum flexibility for change.

The author dismisses this later on:

> What protobuffers are is permissive. They manage to not shit the bed when receiving messages from the past or from the future because they make absolutely no promises about what your data will look like. Everything is optional! But if you need it anyway, protobuffers will happily cook up and serve you something that typechecks, regardless of whether or not it's meaningful.

In real world practice, the permissiveness of Protocol Buffers has proven to be a powerful way to allow for protocols to change over time.

Maybe there's an amazing type system idea out there that would be even better, but I don't know what it is. Certainly the usual proposals I see seem like steps backwards. I'd love to be proven wrong, but not on the basis of perceived elegance and simplicity, but rather in real-world use.

> oneof fields can't be repeated.

(background: A "oneof" is essentially a tagged union -- a "sum type" for type theorists. A "repeated field" is an array.)

Two things:

1. It's that way because the "oneof" pattern long-predates the "oneof" language construct. A "oneof" is actually syntax sugar for a bunch of "optional" fields where exactly one is expected to be filled in. Lots of protocols used this pattern before I added "oneof" to the language, and I wanted those protocols to be able to upgrade to the new construct without breaking compatibility.

You might argue that this is a side-effect of a system evolving over time rather than being designed, and you'd be right. However, there is no such thing as a successful system which was designed perfectly upfront. All successful systems become successful by evolving, and thus you will always see this kind of wart in anything that works well. You should want a system that thinks about its existing users when creating new features, because once you adopt it, you'll be an existing user.

2. You actually do not want a oneof field to be repeated!

Here's the problem: Say you have your repeated "oneof" representing an array of values where each value can be one of 10 different types. For a concrete example, let's say you're writing a parser and they represent tokens (number, identifier, string, operator, etc.).

Now, at some point later on, you realize there's some additional piece of data you want to attach to every element. In our example, it could be that you now want to record the original source location (line and column number) where the token appeared.

How do you make this change without breaking compatibility? Now you wish that you had defined your array as an array of messages, each containing a oneof, so that you could add a new field to that message. But because you didn't, you're probably stuck creating a parallel array to store your new field. That sucks.

In every single case where you might want a repeated oneof, you always want to wrap it in a message (product type), and then repeat that. That's exactly what you can do with the existing design.

The author's complaints about several other features have similar stories.

> One possible argument here is that protobuffers will hold onto any information present in a message that they don't understand. In principle this means that it's nondestructive to route a message through an intermediary that doesn't understand this version of its schema. Surely that's a win, isn't it?

> Granted, on paper it's a cool feature. But I've never once seen an application that will actually preserve that property.

OK, well, I've worked on lots of systems -- across three different companies -- where this feature is essential.

beders•33m ago
If you opt for non-human-readable wire-formats it better be because of very important reasons. Something about measuring performance and operational costs.

If you need to exchange data with other systems that you don't control, a simple format like JSON is vastly superior. You are restricted to handing over tree-like structures. That is a good thing as your consumers will have no problems reading tree-like structures.

It also makes it very simple for each consumer/producer to coerce this data into structs or objects as they please and that make sense to their usage of the data.

You have to validate the data anyhow (you do validate data received by the outside world, do you?), so throwing in coercing is honestly the smallest of your problems.

You only need to touch your data coercion if someone decides to send you data in a different shape. For tree-like structures it is simple to add new things and stay backwards compatible.

Adding a spec on top of your data shapes that can potentially help consumers generate client code is a cherry on top of it and an orthogonal concern.

Making as little assumptions as possible how your consumers deal with your data is a Good Thing(tm) that enabled such useful(still?) things as the WWW.

dinobones•28m ago
lols, the weird protobuf initialization semantics has caused so many OMGs. Even on my team it lead to various hard to debug bugs.

It's a lesson most people learns the hard way after using PBs for a few months.

fsmv•25m ago
I actually really strongly prefer 0 being identical to unset. If you have an unset state then you have to check if the field is unset every time you use it. Using 0 allows you to make all of your code "just work" when you pass 0 to it so you don't need to check at all.

It's like how in go most structs don't have a constructor, they just use the 0 value.

Also oneof is made that way so that it is backwards compatible to add a new field and make it a oneof with an existing field. Not everything needs to be pure functional programming.

zigzag312•19m ago
Among other things, I don't like that they won't support nullable getters/setters:

https://protobuf.dev/design-decisions/nullable-getters-sette...

guzik•11m ago
We thought for a long time about using protobufs in our product [1] and in the end we went with JSON-RPC 2.0 over BLE, base64 for bigger chunks. Yeah, you still need to pass sample format and decode manually. The overhead is fine tho, debugging is way easier (also pulling in all of protobuf just wasn't fun).

[1] aidlab.com/aidlab-2

shdh•6m ago
I just wish protobuf had proper delta compression out of the box
bloppe•6m ago
Protobuf's main design goal is to make space-optimized binary tag-length-value encoding easy. The mentality is kinda like "who cares what the API looks like as long as it can support anything you want to do with TLV encoding and has great performance." Things like oneofs and maps are best understood as slightly different ways of creating TLV fields in a message, rather than pieces of a comprehensive modern type system. The provided types are simply the necessary and sufficient elements to model any fuller type system using TLV.
summerlight•1m ago
https://news.ycombinator.com/item?id=18190005

Just FYI: an obligatory comment from the protobuf v2 designer.

Yeah, protobuf has lots of design mistakes but this article is written by someone who does not understand the problem space. Most of the complexity of serialization comes from implementation compatibility between different timepoints. This significantly limits design space.