> It seems clear enough that protobufs were optimized in a scale that involved a lot of time/cost-sensitive reads and far fewer time/cost-sensitive writes.
Nah, you're assuming too much. Protobuf was thrown together in a fairly ad hoc way by a couple (brilliant!) engineers (Jeff and Sanjay) to help make the Google search index protocol easier to maintain. The specific design decisions in Protobuf were not carefully tested or weighed against other possibilities. They just did something that worked well enough, and it worked well enough that it was rapidly adopted by the rest of the company. It was then too late to change anything.
Yes, the fact that a variable-width size must be written before the data is a kind of big problem, which essentially requires you to make two passes over the message tree, one to compute sizes and one to write the data. There are some clever optimizations that can reduce the impact, but I don't think the designers would make the same decision if starting from scratch with no need to support legacy. But there was no point in history where it was worth breaking compatibility to fix this issue, so that's how it remains. It's a problem, just not that big a problem.
(I maintained Protobuf for several years, including writing version 2 and open sourcing it.)
> The specific design decisions in Protobuf were not carefully tested or weighed against other possibilities.
And just to be clear, I don't think this is bad. On the contrary, I think Protobuf won because it did a wide variety of things "pretty well" while moving quickly and solving real problems. This is how the best technologies are usually made, not by academically trying to perfect everything, but by banging out something that works and running with it to solve real problems. If you try to carefully design everything perfectly upfront, you'll spend a huge amount of time on decisions that don't really matter.
“Rough consensus and running code.” Works pretty well most of the time.
If you're designing a critical component that's going to impact your business indefinitely, you can take some time for due diligence. This is basic Shift Left mentality and it has lots of benefits.
This was 2001-ish. No one at the time had any idea how widely the thing would end up being used, nor how big the company would grow. And spending too much time dwelling on each part of the tech stack then could easily have given a competitor the opportunity to pull ahead, in which case Google wouldn't be what it is today.
It's easy to sit in hindsight and say that all one's actions are justified, because otherwise the present wouldn't have happened. But it's a mistake to justify past actions judging solely on individual outcome. If it had all gone badly and Google had been sunk, these would've been listed as reasons why they should have done things differently. The individual action may be right or wrong in spite of any merit it may garnish.
> This is how the best technologies are usually made, not by academically trying to perfect everything, but by banging out something that works and running with it to solve real problems.
I think this is a really important point. I don't think I've ever told you this before, but I am really impressed by how quickly you turned out proto2. While there are some things here and there that we wish we could change, a lot of it holds up really well. A lot of decisions I made in upb early on, where I thought I was improving on proto2, actually turned out to be bad ideas and the proto2 design was better.
Thanks!
Though in retrospect, I don't know if proto2 was a net win, given all the migration pain it caused. If I were doing it again I would take a more incremental improvement approach on proto1. It would have taken a lot longer but with less pain, I think. That said... who knows if that would have resulted in a better or worse outcome. Open sourcing would have taken a lot longer.
Had I realized at the time that I was taking on a project with no good solutions... heh.
Yes, protobufs are a very interesting and fulfilling technology to work on, but also frustrating because there is so much API exposure that changing any existing API is like trying to run through molasses. A clean break a la proto1->proto2 opens up lots of possibilities on a much shorter time scale, but also creates a heavy migration burden.
There are lot of improvements we can make without touching API, but whenever the API itself is a barrier to further improvements, there are just few good options for managing that.
Your point about incremental changes reminds me of the Linus rant about "bundling", where he argues that incremental changes lead to a better result than a big bang rewrite: https://yarchive.net/comp/linux/bundling.html I very much agree with that approach when possible, but Linux has the benefit of a much narrower API offered to its users. The protobuf API is not only the API of the core library, but of every generated class. The massive surface area just makes any kind of change to generated APIs an enormous challenge (for example, returning string_view from accessors instead of std::string).
Hey that reminds me, I've always wondered: how did it come to be that delimited encoding "won" for sub-messages and group encoding was deprecated? A lot of these problems would go away if things had gone the other way.
Groups can be encoded in one pass, they are efficient to decode (unless you were trying to skip the sub-message, a la LazyField), and they don't have the string/message ambiguity in UnknownFieldSet that messages have.
Do you remember how that came to pass? Maybe some of this happened during the evolution of proto1, which was before my time.
That decision predates me. I think it was basically because early versions of protobuf didn't actually support using a message type as field type, so instead people would declare "string" fields and then manually encode/decode another protobuf type into that field. When the ability to explicitly use message types as field types was added to the language, they wanted to use it in those existing protocols without breaking compatibility, so the design was fit to the pre-existing practice.
I argued for switching to group encoding for submessages when working on proto2, but was shot down. It was a long time ago, but I think the counter-argument was some combination of "it's not worth the breakage" and "the ability to lazily parse sub-messages is too valuable".
Your recollection sounds about right to me.
> Nah, you're assuming too much. Protobuf was thrown together in a fairly ad hoc way by a couple (brilliant!) engineers (Jeff and Sanjay) to help make the Google search index protocol easier to maintain. The specific design decisions in Protobuf were not carefully tested or weighed against other possibilities. They just did something that worked well enough, and it worked well enough that it was rapidly adopted by the rest of the company. It was then too late to change anything.
That's fair enough as a description of how the protocol was (not) designed that those sorts of trade-offs were not taken into account in the initial design.
However, it doesn't entirely invalidate my assumption: search index protocol is pretty obviously a context/scale that would have deeply favored more time/cost-sensitive reads (need to get search results to users as fast as possible) and the time/cost for writers less of a pressure (as writers for the index protocol could presumably be amortized with caching/proxying/throwing hardware at the problem). Whether that was an intentional "optimization" process or simply "optimization evolutionary pressure", it does seem to me (as an entirely outside observer) like a natural trade-off optimization that ocurred at Google scale for protobufs that kept protobufs feeling "well enough" that they were essentially left alone and never optimized for something with different trade-offs (such as use cases that were more write than read-heavy).
Which it is still useful to know those sorts of "optimization evolutionary pressures" to answer questions like "sure, it worked for Google, but will it work for me in this very different use case/scale?"
Yes, I agree with that. If Protobuf hadn't been a reasonably-good design for Google-scale distributed systems, it wouldn't have won (within the company, or beyond). So regardless of how that design came about, we can say it is a reasonably good design overall. But only in aggregate -- not every individual decision can be assumed to be great on its own.