A storage engine's users should never see memtables or SSTables. Today you settle the public surface - Open, Get, Put, Delete, Scan, Close - as one clean facade that hides every internal moving part.
Expose a single DB type with Open, Get, Put, Delete, Scan, and Close as the only public API.
Everything so far has been internal machinery. A storage engine is a library, and its value is the clean contract it offers the code that imports it: open a directory, put and get and delete keys, scan ranges, close. Today you consolidate that contract - Open, Get, Put, Delete, Scan, Close - and make sure nothing below it leaks out. A user should never mention a memtable, an SSTable, or a tombstone to use the store.
This is mostly a facade over methods you already have, but drawing the boundary
explicitly matters: it is what lets the internals keep changing - more levels,
better compaction, block compression - without breaking anyone. The remaining
lessons harden this surface against crashes: making Open rediscover on-disk
tables, Close shut down cleanly, and the whole thing survive a half-finished
write.
// the ONLY exported surface:type DB struct { /* memtable, levels, wal - all unexported */ }func Open(dir string) (*DB, error)func (d *DB) Get(key string) ([]byte, bool)func (d *DB) Put(key string, value []byte) errorfunc (d *DB) Delete(key string) errorfunc (d *DB) Scan(start, end string) Iteratorfunc (d *DB) Close() error