[HN Gopher] Show HN: Skir - like Protocol Buffer but better
___________________________________________________________________
Show HN: Skir - like Protocol Buffer but better
Why I built Skir: https://medium.com/@gepheum/i-spent-15-years-
with-protobuf-t... Quick start: npx skir init All the config
lives in one YML file. Website: https://skir.build GitHub:
https://github.com/gepheum/skir Would love feedback especially
from teams running mixed-language stacks.
Author : gepheum
Score : 49 points
Date : 2026-03-08 17:17 UTC (5 hours ago)
(HTM) web link (skir.build)
(TXT) w3m dump (skir.build)
| poly2it wrote:
| I would recommend exploring OpenRPC for those who have not yet
| seen it. It brings protocol-buffer-like definitions (components),
| RPC definitions and centralised error definitions.
| vvern wrote:
| Notably missing both Go and Rust
| gepheum wrote:
| Absolutely, I am planning to add these 2 as well as C# by June.
| Working on it now.
| dewey wrote:
| > Skir is a universal language for representing data types,
| constants, and RPC interfaces. Define your schema once in a .skir
| file and generate idiomatic, type-safe code in TypeScript,
| Python, Java, C++, and more.
|
| Maybe I'm missing some additional features but that's exactly
| what https://buf.build/plugins/typescript does for Protobuf
| already, with the advantage that you can just keep Protobuf and
| all the battle hardened tooling that comes with it.
| nine_k wrote:
| The entire original post, it seems, is dedicated to explaining
| why Skir is better than plain Protobuf, with examples of all
| the well-known pain points. If these are not persuasive for
| you, staying with Protobuf (or just JSON) should be a fine
| choice.
| waynesonfire wrote:
| Why do I have to work so hard just to understand why I should
| care? Moving me off protobuf requires something at least 10x
| better--that's a very high bar. A library that accomplishes
| such a remarkable feat would slap you in the face with it in
| the first sentence.
| nine_k wrote:
| If you are fine enough with protobufs so that you're not
| actively looking for alternatives, maybe you should not
| spend the effort.
| gepheum wrote:
| +1
|
| Copying from blog post
| [https://medium.com/@gepheum/i-spent-15-years-with-
| protobuf-t...]:
|
| """ Should you switch from Protobuf?
|
| Protobuf is battle-tested and excellent. If your team
| already runs on Protobuf and has large amounts of
| persisted protobuf data in databases or on disk, a full
| migration is often a major effort: you have to migrate
| both application code and stored data safely. In many
| cases, that cost is not worth it.
|
| For new projects, though, the choice is open. That is
| where Skir can offer a meaningful long-term advantage on
| developer experience, schema evolution guardrails, and
| day-to-day ergonomics. """
| gepheum wrote:
| Skir has exactly the same goals as Protobuf, so yes, that
| sentence can apply to Protobuf as well (and buf.build). I
| listed some of the reasons to prefer Skir over Protobuf in my
| humble opinion here:
| https://medium.com/@gepheum/i-spent-15-years-with-protobuf-t...
| Built-in compatibility checks, the fact that you can import
| dependencies from other projects (buf.build offers this, but
| makes you pay for it), some language designs (around
| enum/oneof, the fact that adding fields forces you to update
| all constructor code sites), the dense JSON format, are
| examples.
| drathier wrote:
| https://capnproto.org/ has been my goto since forever. Made by
| the protobuf inventor
| k_g_b_ wrote:
| *Made by the proto2 implementor, Kenton Varda
|
| https://news.ycombinator.com/user?id=kentonv
| jauntywundrkind wrote:
| I've been dabbling with the newer Cap'n Web, whose nicely
| descriptive README's first line says:
|
| > _Cap 'n Web is a spiritual sibling to Cap'n Proto (and is
| created by the same author), but designed to play nice in the
| web stack._
|
| It's just JSON, which has up and down sides. But things like
| promise pipelining are such a huge upside versus _everything_
| else: you can refer to results (and maybe send them around?)
| and kick off new work based on those results, before you even
| get the result back.
|
| This is far far far superior to everything else, totally
| different ball-game.
|
| I've been a little rebuffed by wasm when I try, keep getting
| too close to some gravitational event horizon & get sucked in &
| give up, but for more data-throughput oriented systems, I'm
| still hoping wrpc ends up being a fantastic pick.
| https://github.com/bytecodealliance/wrpc . Also Apache Arrow
| Flight, which I know less about, has mad traction in serious
| data-throughput systems, which being adjacent to amazingly
| popular Apache Arrow makes sense.
| https://arrow.apache.org/docs/format/Flight.html
| jeffbee wrote:
| Obligatory dense field numbers seems like a massive downside, the
| problems of which would become evident after a busy repo has been
| open for a few days.
| gepheum wrote:
| It's not obligatory. Basically Protobuf gives you a choice
| between (1) binary format, (2) readable JSON. Skir gives you a
| choice between (1) binary format, (2) readable JSON, (3) dense
| JSON. It recommends dense JSON as the "default choice", but it
| does not force it. The reason why it's recommended as the
| default choice is because it offers a good tradeoff between
| efficiency (only a bit less compact than binary), backward
| compatibility (you can rename fields safely unlike readable
| JSON) and debuggability (although it's definiely not as good as
| readable JSON, because you lose the field numbers, it's decent
| and much better than binary format)
| refulgentis wrote:
| I spent some time in the actual compiler source. There's real
| work here, genuinely good ideas.
|
| The best thing Skir does is strict generated constructors. You
| add a field, every construction site lights up. Protobuf's
| "silently default everything" model has caused mass production
| incidents at real companies. This is a legitimately better
| default.
|
| Dense JSON is interesting but the docs gloss over the tradeoff:
| your serialized data is [3, 4, "P"]. If you ever lose your
| schema, or a human needs to read a payload in a log, you're
| staring at unlabeled arrays. Protobuf binary has the same problem
| but nobody markets binary as "easy to inspect with standard
| tools." The "serialize now, deserialize in 100 years" claim has a
| real asterisk. Compatibility checking requires you to opt into
| stable record IDs and maintain snapshots. If you skip that (and
| the docs' own examples often do), the CLI literally warns you:
| "breaking changes cannot be detected." So it's less "built-in
| safety" and more "safety available if you follow the discipline."
| Which is... also what Protobuf offers.
|
| The Rust-style enum unification is genuinely cleaner than
| Protobuf's enum/oneof split. No notes there, that's just better
| language design.
|
| Minor thing that bothered me disproportionately: the constant
| syntax in the docs (x = 600) doesn't match what the parser
| actually accepts (x: 600).
|
| The weirdest thing that bugged the heck out of me was the
| tagline, "like protos but better", that's doing the project no
| favors.
|
| I think this would land better if it were positioned as
| "Protobuf, but fresh" rather than "Protobuf, but better." The
| interesting conversation is which opinions are right, not whether
| one tool is universally superior.
|
| Quite frankly, I don't use protobuf because it seems like an
| unapproachable monolith, and I'm not at FAANG anymore, just a
| solo dev. No one's gonna complain if I don't. But I do love the
| idea of something simpler thats easy to wrap my mind around.
|
| That's why "but fresh" hits nice to me, and I have a feeling it
| might be more appealing than you'd think - ex. it's hard to
| believe a 2 month old project is _strictly better_ than whatever
| mess and history protobufs gone through with tons of engineers
| paid to use and work on it. It is _easy_ to believe it covers 99%
| of what Protobuf does already, and any crazy edge cases that pop
| up (they always do, eventually :), will be easy to understand and
| fix.
| vineyardmike wrote:
| > Minor thing that bothered me disproportionately: the constant
| syntax in the docs (x = 600) doesn't match what the parser
| actually accepts (x: 600).
|
| You're a better man than me. If the docs can't even get the
| syntax right, that's a hard no from me.
|
| Also, fwiw, you've got a few points wrong about protos.
| Inspecting the binary data is hard, but the tag numbers are
| present. You need the schema, but at least you can identify
| each element.
|
| Also, I disagree on the constructor front. Proto forces you to
| grapple with the reality that a field may be missing. In a
| production system, when adding a new field, there _will_ be a
| point where that field isn't present on only one side of the
| network call. The compiler isn't saving you.
|
| Fresh is more honest than better, and personally, I wouldn't
| change it.
| gepheum wrote:
| > Also, I disagree on the constructor front. Proto forces you
| to grapple with the reality that a field may be missing. In a
| production system, when adding a new field, there will be a
| point where that field isn't present on only one side of the
| network call. The compiler isn't saving you.
|
| I agree it's important for users to understand that newer
| fields won't be set when they deserialize old data -- whether
| that's with Protobuf or Skir. I disagree with the idea that
| not forcing you to update all constructor call sites when you
| add a field will help (significantly) with that. Are you
| saying that because Protobuf forces you to manually search
| for all call sites when you add a field, it forces you to
| _think_ about what happens if the field is not set at
| deserialization, hence, it 's a good thing? I'm not sure that
| outweighs the cost of bugs introduced by cases where you
| forget to update a constructor call site when you add a field
| to your schema.
| vineyardmike wrote:
| Respectfully, I've never _forgotten_ a call site, but also
| yes. In a hypothetical HelloWorld service, the HelloRequest
| and HelloResponse generally aren't used anywhere except a
| rpc caller and rpc handler, so it's not hard to "remember"
| and find the usage.
|
| Some callers may not need to update right away, or don't
| need the new feature at all, and breaking the existing
| callers compilation is bad. If your caller is a different
| team, for example, and their CICD breaks because you added
| a field, that's bad. Each place it's used, you should think
| about how it'll be handled, BUT ALSO, your system
| explicitly should gracefully handle the case where it's not
| uniformly present. It's an explicit goal of protos to
| support the use case where heterogeneous schema versions
| are used over the wire.
|
| If a bug is introduced because the caller and handler use
| different versions, the compiler wasn't going to save you
| anyways. That bug would have shown up when you deploy or
| update the client and server anyways - unless you
| atomically update both at once. You generally cannot
| guarantee that a client won't use an outdated version of
| the schema, and if things break because of that, you didn't
| guard it correctly. That's a business logic failure not a
| compilation failure.
| gepheum wrote:
| Thank you so much for taking the time to dig into the compile
| source code and the thorough comment you left.
|
| For dense JSON: the idea is that it is often a good "default"
| choice because it offers a good tradeoff across 3 properties:
| efficiency (where it's between binary and readable JSON),
| persistability (safe to evolve shema without losing backward
| compatibility), and readability (it's low for the reasons you
| mentioned, but it's not as bad as a binary string). I tried to
| explain this tradeoff in this table:
| https://skir.build/docs/serialization#serialization-formats
|
| I hear your point about the tagline "like protos but better"
| which I hesitated to put because it sounds presumptuous. But I
| am not quite sure what idea you mean to convey by "fresh"?
| computomatic wrote:
| Not the parent but I infer "fresh" as meaning a new approach
| to an old problem (with the benefits of experience baked in).
| A synonym of "modern" without the baggage.
| gepheum wrote:
| Fair. I changed the tagline on the website to "A modern
| alternative to Protocol Buffer". Thanks for the feedback.
| refulgentis wrote:
| 100%, danke
| refulgentis wrote:
| Cheers :) (other replier was right on "fresh", "fresh"
| definitely wasn't right)
| gepheum wrote:
| Also, thank you for flagging the constant syntax problem (x =
| 600) on the website. Fixed.
| nazgu1 wrote:
| If I may suggest, Swift support will be more than appreciated, to
| consider it for a viable protocol for connecting backend with
| mobile applications.
| gepheum wrote:
| Yeah totally fair. I targeted Dart because of Flutter, but I
| think I will include Swift in the next wave of languages, after
| Rust, Go and C#.
| chocolatkey wrote:
| That "compact JSON" format reminds me if the special protobufs
| JSON format that Google uses in their APIs that has very little
| public documentation. Does anyone happen to know why Google uses
| that, and to OP, were you inspired by that format?
| gepheum wrote:
| I think you may be referring to JSPB. It's used internally at
| Google but has little support in the open-source. I know about
| it, but I wouldn't say I was inspired by it. It's particularly
| unreadable, because it needs to account for field numbers being
| possible sparse. Google built it for frontend-backend
| communication, when both the frontend and the backend use
| Protobuf dataclasses, as it's more efficient than sending a
| large JSON object and also it's faster to deserialize than
| deserializing a binary string on the browser side. I think it's
| mostly deprecated nowadays.
| woadwarrior01 wrote:
| Also, flexbuffers.
| argon81 wrote:
| How does this compare with https://connectrpc.com/ as that
| project seems to share similar goals
| elvin_d wrote:
| Like this but zero copy, easy migration/versioning, Rust and WASM
| support.
| lasgawe wrote:
| Looks nice. But what are the use cases of this? I'm still trying
| to figure that out.
| gepheum wrote:
| Thanks! Main use case (similarly to Protobuf) is when you need
| to exchange data types between systems written in different
| languages. Like Protobuf, it can also be used in a mono-
| linguistic system, when you want to serialize systems and have
| strong guarantees that you will be able to deserialize your
| data in the future (when you use classic serialization
| libraries like Pydantic, Java Serialization etc., it's easy to
| accidentally modify a schema and break the ability to
| deserialize old data.)
| il-b wrote:
| Impressive. Some interop with established standards such as
| OpenAPI or gRPC would make it an easier sell for non-greenfield
| projects
| gepheum wrote:
| Thanks. Definitely agree, will try to think about what that
| could look like.
| shalabhc wrote:
| Did you look at other formats like Avro, Ion etc? Some feedback:
|
| 1. Dense json
|
| Interesting idea. You can also just keep the compact binary if
| you just tag each payload with a schema id (see Avro). This also
| allows a generic reader to decode any binary format by reading
| the schema and then interpreting the binary payload, which is
| really useful. A secondary benefit is you never ever misinterpret
| a payload. I have seen bugs with protobufs misinterpreted since
| there is no connection handshake and interpretation is akin to
| 'cast'.
|
| 2. Compatibility checks
|
| +100 there's not reason to allow breaking changes by default
|
| 3. Adding fields to a type: should you have to update all call
| sites?
|
| I'm not so sure this is the right default. If I add a field to a
| core type used by 10 services, this requires rebuilding and
| deploying all of them.
|
| 4. enum looks great. what about backcompat when adding new enum
| fields? or sometimes when you need to 'upgrade' an atomic to an
| enum?
| gepheum wrote:
| Thanks for the feedback.
|
| 0. Yes, I looked at Avro, Ion. I like Protobuf much better
| because I think using field numbers for field identity, meaning
| being able to rename fields freely, is a must.
|
| 1. Yes. Skir also supports that with binary format (you can
| serialize and deserialize a Skir schema to JSON, which then
| allows you to convert from binary format to readable JSON). It
| just requires to build many layers of extra tooling which can
| be painful. For example, if you store your data in some SQL
| engine X, you won't be able to quickly visualize your data with
| a simple SELECT statement, you need to build the tooling which
| will allow you to visualize the data. Now dense JSON is
| obviously not idea for this use case, because you don't see the
| field names, but for quick debugging I find it's "good enough".
|
| 3. I agree there are definitely cases where it can be painful,
| but I think the cases where it actually is helpful are more
| numerous. One thing worth noting is that you can "opt-out" of
| this feature by using `ClassName.partial(...)` instead of
| `ClassName()` at construction time. See for example
| `User.partial(...)` here:
| https://skir.build/docs/python#frozen-structs I mostly added
| this feature for unit tests, where you want to easily create
| some objects with only some fields set and not be bothered if
| new fields are added to the schema.
|
| 4. Good question. I guess you mean "forward compatibility": you
| add a new field to the enum, not all binaries are deployed at
| the same time, and some old binary encounters the new enum it
| doesn't know about? I do like Protobuf does: I default to the
| UNKNOWN enum. More on this: - https://skir.build/docs/schema-
| evolution#adding-variants-to-... -
| https://skir.build/docs/schema-evolution#default-behavior-dr...
| - https://skir.build/docs/protobuf#implicit-unknown-variant
| shalabhc wrote:
| > meaning being able to rename fields freely, is a must.
|
| avro supports field renames though.
|
| 3. on second thought i believe you'd only have to deploy when
| you choose. the next build will force you to provide values
| (or opt into the default). so forcing inspection of
| construction sites seems good.
___________________________________________________________________
(page generated 2026-03-08 23:00 UTC)