# Bond ## Overview Bond is a cross-platform, schema-first serialization framework developed by Microsoft. It defines data schemas in `.bond` files that are compiled into language-specific classes, supporting C#, C++, Python, and Java. Bond provides three binary protocols (Compact Binary, Fast Binary, and Simple Binary) and a JSON protocol. It excels in scenarios requiring high-performance serialization with schema evolution guarantees, making it popular in large-scale distributed systems at Microsoft. Bond handles schema versioning gracefully, allowing fields to be added or removed without breaking existing consumers. ## Schema Definition Define your data structures in `.bond` schema files. ``` // schemas/models.bond namespace Example.Models; enum Priority { Low = 0, Medium = 1, High = 2, Critical = 3 } struct Address { 0: string Street; 1: string City; 2: string State; 3: string ZipCode; 4: string Country = "US"; } struct Person { 0: string FirstName; 1: string LastName; 2: uint32 Age; 3: nullable
HomeAddress; 4: vector EmailAddresses; 5: map Metadata; 6: Priority Priority = Medium; } ``` ## Compact Binary Serialization Use Compact Binary protocol for the best balance of size and speed. ```csharp using Bond; using Bond.IO.Unsafe; using Bond.Protocols; using Example.Models; // Create an object var person = new Person { FirstName = "Alice", LastName = "Smith", Age = 30, HomeAddress = new Address { Street = "123 Main St", City = "Seattle", State = "WA", ZipCode = "98101" }, EmailAddresses = { "alice@example.com", "alice@work.com" }, Metadata = { ["department"] = "Engineering" }, Priority = Priority.High }; // Serialize to Compact Binary var output = new OutputBuffer(); var writer = new CompactBinaryWriter(output); Serialize.To(writer, person); byte[] serializedBytes = output.Data.ToArray(); // Deserialize from Compact Binary var input = new InputBuffer(serializedBytes); var reader = new CompactBinaryReader(input); Person deserialized = Deserialize.From(reader); ``` ## Fast Binary Serialization Use Fast Binary for maximum deserialization speed when payload size is less critical. ```csharp using Bond; using Bond.IO.Unsafe; using Bond.Protocols; using Example.Models; var person = new Person { FirstName = "Bob", LastName = "Jones", Age = 25 }; // Fast Binary - optimized for deserialization speed var output = new OutputBuffer(); var writer = new FastBinaryWriter(output); Serialize.To(writer, person); byte[] fastBytes = output.Data.ToArray(); // Deserialize var input = new InputBuffer(fastBytes); var reader = new FastBinaryReader(input); Person result = Deserialize.From(reader); ``` ## JSON Protocol Use the JSON protocol for debugging, logging, or interop with non-Bond systems. ```csharp using Bond; using Bond.IO.Unsafe; using Bond.Protocols; using Example.Models; using System.IO; using System.Text; var person = new Person { FirstName = "Carol", LastName = "Davis", Age = 35, EmailAddresses = { "carol@example.com" } }; // Serialize to JSON using var textWriter = new StringWriter(); var jsonWriter = new SimpleJsonWriter(textWriter); Serialize.To(jsonWriter, person); jsonWriter.Flush(); string json = textWriter.ToString(); // Deserialize from JSON var jsonReader = new SimpleJsonReader( new StringReader(json)); Person fromJson = Deserialize.From(jsonReader); ``` ## Schema Evolution Bond supports adding and removing fields while maintaining compatibility. ```csharp using Bond; using Bond.IO.Unsafe; using Bond.Protocols; // Version 1 schema // struct Order { 0: string Id; 1: double Amount; } // Version 2 schema adds a new field // struct Order { 0: string Id; 1: double Amount; 2: string Currency = "USD"; } // Data serialized with V1 can be deserialized with V2 // The new field gets its default value ("USD") // Data serialized with V2 can be deserialized with V1 // The unknown field (Currency) is safely skipped // Example: transcoding between protocols with schema migration var inputBuffer = new InputBuffer(v1SerializedData); var reader = new CompactBinaryReader(inputBuffer); var outputBuffer = new OutputBuffer(); var writer = new CompactBinaryWriter(outputBuffer); // Transcode automatically handles schema differences Transcode, CompactBinaryWriter> .From(reader, writer); ``` ## Service Integration Pattern Register Bond serialization as a service for dependency injection. ```csharp using Bond; using Bond.IO.Unsafe; using Bond.Protocols; public interface IBondSerializer { byte[] Serialize(T obj) where T : new(); T Deserialize(byte[] data) where T : new(); } public sealed class CompactBondSerializer : IBondSerializer { public byte[] Serialize(T obj) where T : new() { var output = new OutputBuffer(); var writer = new CompactBinaryWriter(output); Bond.Serialize.To(writer, obj); return output.Data.ToArray(); } public T Deserialize(byte[] data) where T : new() { var input = new InputBuffer(data); var reader = new CompactBinaryReader(input); return Bond.Deserialize.From(reader); } } // Registration builder.Services.AddSingleton(); ``` ## Protocol Comparison | Protocol | Serialization Speed | Deserialization Speed | Payload Size | Human Readable | |----------|-------------------|-----------------------|-------------|----------------| | Compact Binary | Fast | Fast | Smallest | No | | Fast Binary | Fast | Fastest | Larger | No | | Simple Binary | Fastest | Fast | Medium | No | | Simple JSON | Slow | Slow | Largest | Yes | ## Best Practices 1. **Define all data contracts in `.bond` schema files**: use the Bond code generator (`gbc`) to produce C# classes rather than hand-coding `[Bond.Schema]` attributes, ensuring schema consistency across languages. 2. **Assign explicit field ordinals starting from 0**: every field must have a unique, stable ordinal number that never changes once published; use gaps (0, 1, 5, 10) to reserve space for future fields. 3. **Use Compact Binary as the default wire format**: it provides the best tradeoff between serialization speed and payload size for most production workloads. 4. **Set meaningful default values for new fields**: when evolving a schema, new fields with defaults allow old consumers to deserialize new data without errors. 5. **Never reuse or reassign field ordinals**: removing a field should retire its ordinal permanently; reusing it with a different type breaks backward compatibility. 6. **Use `nullable` for optional complex fields**: this allows the field to be absent on the wire and avoids allocating empty objects during deserialization. 7. **Wrap Bond serialization behind an interface**: inject `IBondSerializer` so you can swap protocols (Compact to Fast) or migrate to a different serialization library without changing business logic. 8. **Benchmark serialization in your specific workload**: use BenchmarkDotNet to compare Compact vs. Fast vs. Simple Binary for your actual data shapes, as performance varies by schema complexity. 9. **Version your `.bond` schema files in source control**: treat schema files as API contracts with pull request reviews, semantic versioning, and breaking-change detection in CI. 10. **Use transcoding for protocol conversion**: when bridging systems that use different Bond protocols, use `Transcode` instead of deserialize-then-reserialize to avoid unnecessary object allocation.