<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Luke Miles</title>
    <description>The latest articles on DEV Community by Luke Miles (@qpwo).</description>
    <link>https://dev.to/qpwo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F247332%2Fd671ba42-3ab7-40fb-9338-ec4cb5197d5c.jpeg</url>
      <title>DEV Community: Luke Miles</title>
      <link>https://dev.to/qpwo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/qpwo"/>
    <language>en</language>
    <item>
      <title>WHY THEY DID NOT KNOW, WHY THE MACHINE WAS NEARLY RIGHT, AND HOW TO WASH THE ACCIDENTS AWAY</title>
      <dc:creator>Luke Miles</dc:creator>
      <pubDate>Sat, 18 Jul 2026 05:41:45 +0000</pubDate>
      <link>https://dev.to/qpwo/why-they-did-not-know-why-the-machine-was-nearly-right-and-how-to-wash-the-accidents-away-5346</link>
      <guid>https://dev.to/qpwo/why-they-did-not-know-why-the-machine-was-nearly-right-and-how-to-wash-the-accidents-away-5346</guid>
      <description>&lt;p&gt;Jep Tea 5.6&lt;br&gt;
Almost unedited&lt;/p&gt;

&lt;p&gt;they were optimizing under a different physical world, a different economic world, and a different scale of software. They knew their machines extraordinarily well. What they did not possess was evidence from a future in which one program can simultaneously command hundreds of CPU hardware threads, terabytes of addressable memory, several independent network interfaces, an array of NVMe devices, and a rack’s worth of accelerator arithmetic presented as a single job. They could not optimize a language for workloads that did not yet exist, for failure modes that had not yet had enough hardware on which to emerge, or for the cumulative weight of abstraction layers that had not yet been invented.&lt;/p&gt;

&lt;p&gt;Machine code was perfect in the narrow, important sense that it told the truth.&lt;/p&gt;

&lt;p&gt;A machine instruction is not a promise that some other layer will eventually attempt to perform an operation. It is the operation admitted by the processor contract. The instruction identifies registers, an addressing form, an immediate, a branch relation, a vector width, a memory-order consequence, or a transition in architectural state. Its hidden implementation may be immense—decoders, rename tables, schedulers, reorder buffers, predictors, execution ports, cache hierarchies, translation machinery, coherency agents, microcode, and power controls—but its visible meaning is finite. The contract is measurable. One can count bytes, instructions, dependencies, cache lines, loads, stores, branches, vector lanes, and retirement events. One can profile it without asking five frameworks what they renamed the operation.&lt;/p&gt;

&lt;p&gt;CPU architects are geniuses because this finite contract must survive hostile constraints simultaneously. It must remain compatible with old binaries while exploiting new transistors. It must accept a compact instruction stream, discover parallelism that the stream often fails to state, rename false dependencies, speculate past unresolved control flow, recover precisely from faults, preserve an architectural memory model, arbitrate shared caches, tolerate variable memory latency, and deliver useful work inside a power envelope. The architect and microarchitect must make a physically timed object behave like a clean abstract machine. Every instruction is therefore an extraordinary compression boundary: a few bytes can request work whose implementation spans millions or billions of transistors.&lt;/p&gt;

&lt;p&gt;Machine code is also honest about cost in a way that higher layers frequently are not. A load names an access. A branch creates a control dependency. A vector instruction exposes lane count. An atomic instruction announces that coherency and ordering will be involved. A system-call instruction announces a privilege transition. Nothing in the encoding says “free.” Even when exact latency varies by microarchitecture, the program is close enough to the mechanism that the variance can be measured and attributed.&lt;/p&gt;

&lt;p&gt;It was not literally flawless. Instruction sets carry historical baggage. Some encodings are irregular. Some operations are awkward because compatibility won. Some memory models are difficult. Some vector transitions have penalties. Some instructions exist because a market demanded them and later became fossils. “Perfect” here means that machine code is the last software representation before physics takes over, and therefore it has the least room to lie about where work happens.&lt;/p&gt;

&lt;p&gt;Assembly was almost perfect because it kept that truth and gave names to the unbearable parts.&lt;/p&gt;

&lt;p&gt;Assembly replaced numeric opcodes with mnemonics, numeric branch destinations with labels, raw relocation arithmetic with symbols, and repeated instruction sequences with macros. It made the machine contract writable without inserting a mandatory runtime. A label did not allocate. A macro did not necessarily exist after assembly. A register name still meant a register. A load still meant a load. An instruction still had a visible encoding and a visible dependency structure. The assembler helped with bookkeeping while preserving the programmer’s right to know exactly what would execute.&lt;/p&gt;

&lt;p&gt;That is why assembly feels nearly ideal for Nbl. It has very few semantics. It is naturally static. It allows CPU and device instruction streams to be embedded without pretending they are ordinary function calls. It has no requirement for a heap, garbage collector, exception unwinder, reflection database, dynamic loader, object model, or scheduler. It can express a memory-mapped peripheral, a packet checksum, a tensor-core instruction, a cache-line flush, or a sleep instruction without translating the request through a hierarchy of abstractions.&lt;/p&gt;

&lt;p&gt;Assembly was only almost perfect because it failed to carry enough meaning forward. A register name says where a value is now, not what the value means everywhere. A pointer says where bytes begin, not the shape, extent, ownership, lifetime, alignment, access domain, NUMA node, device, or synchronization discipline of the object. A label says where control can go, not why it is permitted to go there. A sequence of multiply-add instructions does not say which named dimensions are being contracted. The programmer knows the tensor; the assembler sees addresses. The optimizer cannot reliably reconstruct all the meaning that was discarded before the instructions were written.&lt;/p&gt;

&lt;p&gt;Assembly also repeats facts. A matrix extent appears in allocation arithmetic, loop limits, address calculations, launch geometry, boundary masks, communication counts, and validation code. Repetition creates disagreement. One copy changes and another does not. The machine then executes both contradictory claims faithfully. Assembly rarely causes a lie itself; it makes it easy for the human to tell the machine two different truths.&lt;/p&gt;

&lt;p&gt;C99 was almost perfect because it preserved most of the machine while making composition bearable.&lt;/p&gt;

&lt;p&gt;C gave names to scalar types, aggregate layouts, functions, expressions, control flow, and storage duration. It made separate compilation practical. It made calling conventions and data structures portable enough to build operating systems and network stacks while retaining escape hatches to assembly and raw memory. C99 added useful precision: fixed-width integer types through the standard headers, designated initializers, compound literals, restricted pointers, inline functions, variable declarations near use, and a clearer basis for numerical work. Its abstract machine remained small enough that a competent programmer could usually predict the emitted instructions.&lt;/p&gt;

&lt;p&gt;The central wound was the separation of an array’s address from its extent.&lt;/p&gt;

&lt;p&gt;An array object in C has a size at the point where it is declared. The compiler can apply sizeof there. But in most expressions the array is converted to a pointer to its first element. In a function parameter, syntax that resembles an array parameter is adjusted to pointer type. The callee therefore receives an address while the length travels separately, if it travels at all. The address has escaped from the fact that makes the address safe.&lt;/p&gt;

&lt;p&gt;Arrays should have known their own length in the sense that the language should have kept address and extent as one statically meaningful object. This does not require a heap header before every allocation, a dynamic descriptor, or a fat pointer in every register. For a statically shaped program, the extent can be part of the type and exist only at compile time. A parameter can mean “a reference to exactly [B, token, head, dimension] elements of bf16, aligned to 128 bytes, owned by device 3 for this phase.” The generated calling convention may still pass one machine address because every other fact is already known globally. The language can retain the proof without charging the runtime.&lt;/p&gt;

&lt;p&gt;Why did C not do that?&lt;/p&gt;

&lt;p&gt;Because C grew from a lineage and an environment where a pointer-plus-convention was an excellent bargain. Early systems had tiny memories, slow compilers, simple ABIs, and programs whose entire working set could be understood by one person. Passing an address was cheap. Copying arrays was not. Encoding every extent into a type system would have complicated compatibility, separate compilation, and generic routines. Many important data structures were intentionally irregular. Operating-system code needed to interpret device buffers, protocol packets, and storage blocks whose layouts were determined externally. A language that insisted on rich descriptors could have made the work harder or more expensive on the machines of the time.&lt;/p&gt;

&lt;p&gt;C’s designers also valued a rule that still matters: do not make a programmer pay for information the implementation does not need at runtime. The mistake was not refusing universal runtime descriptors. The mistake, visible only later, was allowing compile-time meaning to disappear merely because runtime representation remained small.&lt;/p&gt;

&lt;p&gt;Variable-length arrays in C99 did not heal this wound. They allowed certain extents to be computed at runtime and used in types within a scope. They did not make every array a self-describing, first-class shaped value with universal ownership and lifetime. They added runtime variability where NBL wants compile-time certainty.&lt;/p&gt;

&lt;p&gt;They did not know why every symbol would eventually need exactly one universal meaning.&lt;/p&gt;

&lt;p&gt;Traditional scope is a powerful compression device. A programmer can use i in one loop and i in another because the loops are short, local, and visually separate. A library can use count for bytes while another uses count for records because separate compilation isolates them. Overloading lets one mathematical name apply to many types. Shadowing lets a local detail replace an outer one. These were reasonable conveniences when programs were mostly handwritten, call graphs were shallower, generated code was rare, and debugging happened close to the source.&lt;/p&gt;

&lt;p&gt;At modern scale, reused names become semantic aliasing. The same token can mean a token position, a vocabulary identifier, a network token, an authentication token, or a scheduler credit. row can mean a matrix row, a shard owner, a database row, a packet ring slot, or a GPU cooperative-thread-array row. rank can mean a distributed process, a matrix rank, a BPE merge priority, or a position in a sorted set. Every reuse asks context to repair meaning that the name discarded.&lt;/p&gt;

&lt;p&gt;Context repair is expensive. It consumes human attention. It confuses logs. It makes profiler labels collide. It makes generated code unreadable. It makes cross-layer tracing depend on brittle conventions. It makes static analysis ask whether two same-spelled objects are related when the programmer already knows they are not. It makes a compiler carry symbol identities that the source keeps trying to blur.&lt;/p&gt;

&lt;p&gt;A universal name is not merely a stylistic preference. It is a join key across the entire system. The same name can connect source, lowered intermediate representation, machine registers, memory regions, launch descriptors, counters, traces, packet fields, checkpoint entries, and proof obligations. If gpt_attention_query_head always means one axis, a profiler can aggregate it without guessing. If finewire_file_byte always means the byte axis of a 200001024-byte shard, the network path, checksum path, mmap path, and storage path cannot silently disagree about its extent.&lt;/p&gt;

&lt;p&gt;Earlier designers did not build for a world where programs would be generated from graphs containing tens of thousands of nodes, lowered through many intermediate representations, partitioned across devices, and then diagnosed from distributed traces. Local scope was enough for local programs. Universal semantic identity becomes valuable when the program is no longer local in any useful sense.&lt;/p&gt;

&lt;p&gt;They did not know why arrays would need to be statically shaped because dynamic structures solved the urgent problems in front of them.&lt;/p&gt;

&lt;p&gt;General-purpose software receives data that is not known in advance. Files vary. Packets vary. users type arbitrary strings. Trees are irregular. Graphs are sparse. Processes come and go. Dynamic allocation and dynamic extents were not indulgences; they were necessary tools. Memory was scarce enough that allocating for a maximum could be absurd. Compilers could not specialize a whole program around every possible shape. Hardware did not reward regular tensor tiles as dramatically as modern SIMD and accelerator hardware does.&lt;/p&gt;

&lt;p&gt;Static shape becomes dominant when the workload itself has regular geometry and when data movement is the limiting resource. A fixed shape lets the compiler decide, before execution, every allocation, alignment, stride, tile, vector width, launch geometry, DMA descriptor, queue depth, shard boundary, and ownership transition. It can prove that two buffers do not overlap. It can know the maximum resident set. It can place pages on NUMA nodes. It can reserve huge pages. It can choose whether a tensor belongs in registers, shared memory, HBM, host DRAM, persistent memory, or an SSD staging region. It can reject a program whose live ranges exceed the machine.&lt;/p&gt;

&lt;p&gt;A dynamic dimension is not one unknown number. It is a fan-out of uncertainty. The allocator must handle it. Every consumer must carry it. Bounds checks must consult it. Launch geometry must derive from it. Communication sizes must serialize it. Kernels may branch on it. Caches see more variants. Compilers generate guards and fallback graphs. Profilers aggregate incomparable executions. A single unknown extent can turn one program into a family of programs selected at runtime.&lt;/p&gt;

&lt;p&gt;NBL’s answer is not that the outside world has no variability. The answer is that variability must be converted at the boundary into one of a finite set of statically shaped cases. A packet is placed into a fixed slot. A document is admitted only if it fits a declared maximum, or it is routed to a separately compiled larger case. A sequence length selects one precompiled graph. A batch stage is a different fixed program. The uncertainty is not allowed to leak through every operation.&lt;/p&gt;

&lt;p&gt;They did not know about the ordinary possibility of a 128-core CPU, terabytes of RAM, four network cards, eight SSDs, and sixteen GPUs participating in one application.&lt;/p&gt;

&lt;p&gt;Each number changes more than capacity.&lt;/p&gt;

&lt;p&gt;A 128-core CPU is not a faster single CPU. It is a topology. Cores share some caches and not others. They attach to memory controllers through particular fabrics. They receive interrupts through particular routes. They contend for translation structures, coherency bandwidth, last-level cache capacity, and memory channels. The difference between a local and remote NUMA access can dominate arithmetic. A cache line written by many cores becomes a moving ownership token. A global queue becomes a machine for transporting that line across the socket.&lt;/p&gt;

&lt;p&gt;Terabytes of RAM are not merely a larger array. Page tables become large structures in their own right. TLB reach matters. Page faults become catastrophic in a latency-sensitive path. Zeroing and initializing memory become measurable jobs. A full scan can consume seconds and saturate memory channels. A leak that was harmless at megabyte scale can survive long enough to become operational policy. Checkpointing a terabyte is a storage and network event, not a function call.&lt;/p&gt;

&lt;p&gt;Four network cards are not one network card multiplied by four. They have separate PCIe paths, receive-side scaling tables, completion queues, interrupt vectors, DMA engines, firmware, link partners, and failure modes. Traffic must be steered. Flows can become imbalanced. The CPU cores handling completions should be near the correct NUMA memory and PCIe root. An application that ignores topology can copy every packet across the socket before processing it. The network can be faster than a naive memory path.&lt;/p&gt;

&lt;p&gt;Eight SSDs are not a directory with eight times the space. They expose parallel submission and completion queues. Queue depth determines whether internal flash parallelism is used. Filesystem metadata can serialize paths that data could have served independently. Write amplification, garbage collection, thermal throttling, and failure domains matter. Striping can produce bandwidth; it can also turn one device’s tail latency into every request’s tail latency. Direct I/O, mmap, buffered I/O, and io_uring each move responsibility to a different layer.&lt;/p&gt;

&lt;p&gt;Sixteen GPUs are not one GPU with more multiprocessors. They have separate memories and separate schedulers. Some pairs may have direct high-bandwidth links; others communicate through PCIe switches, CPUs, NICs, or multiple hops. Collective communication becomes an algorithm with a topology, not a library call. A tensor’s placement is part of its meaning. A gradient reduced on the wrong path can cost more than the matrix multiplication that produced it.&lt;/p&gt;

&lt;p&gt;The old language model—one process, one mostly uniform memory, one call stack, one heap, one implicit scheduler—cannot adequately describe this machine. It can operate it only by stacking libraries and runtimes above the language. Each library invents descriptors for the meaning the language failed to retain. The result is a tower of substitute type systems, substitute schedulers, substitute allocators, substitute ownership rules, and substitute profilers.&lt;/p&gt;

&lt;p&gt;They did not know the load the motherboard would carry.&lt;/p&gt;

&lt;p&gt;The motherboard is not passive scenery. It is where power, clocks, lanes, interrupts, DMA, coherency, and heat meet.&lt;/p&gt;

&lt;p&gt;A modern high-end board must distribute enormous current at low voltage while maintaining transient stability as cores and accelerators change activity. It must route high-speed differential pairs with controlled impedance and tolerable loss. It must allocate PCIe lanes among GPUs, NICs, NVMe devices, switches, and chipset links. It must expose enough memory channels and DIMM slots while preserving signal integrity. It must provide clock references, reset sequencing, sideband management, firmware storage, and out-of-band control. Every connector and switch adds insertion loss, latency, power, and another component that can fail.&lt;/p&gt;

&lt;p&gt;Software places load on all of this. A badly scheduled all-to-all can make every GPU and NIC inject traffic at once. A shared counter can turn the coherency fabric into a ping-pong engine. An allocator can make every core fault and zero pages simultaneously. A checkpoint can cause GPUs to drain into host memory, host memory to drain into SSDs and NICs, and every PCIe path to compete at once. A polling design can keep cores in high-power states while a thermal event reduces device clocks, lengthening queues and increasing the duration of the load.&lt;/p&gt;

&lt;p&gt;The board carries electrical load, thermal load, traffic load, and coordination load. The programming language normally names none of them. It says pointer, thread, and file. The actual machine sees root complexes, NUMA domains, queue pairs, BAR mappings, page pins, IOMMU translations, cache-line ownership, and packet bursts.&lt;/p&gt;

&lt;p&gt;They did not know how bad things could get because pathologies multiply rather than add.&lt;/p&gt;

&lt;p&gt;A lock by itself may cost little. Put it on a cache line shared across sockets and the line moves. Let the lock holder be preempted and every waiter stalls. Let the waiters spin and they consume execution and coherency bandwidth while making no progress. Put the protected allocation on a page that faults and the convoy waits through kernel work. Let the stalled stage feed a bounded queue and upstream producers block. Let those producers hold other locks and the dependency graph becomes invisible. Add retries at a network boundary and duplicated work arrives precisely when the system is least able to absorb it.&lt;/p&gt;

&lt;p&gt;A small shape mismatch can force a copy. The copy can break alignment. Broken alignment can select a slower kernel. The slower kernel can extend an activation’s lifetime. The longer lifetime can raise peak memory. Higher peak memory can cause allocator fragmentation or spill. The spill can cross PCIe. The longer step can make one distributed rank a straggler. Every other rank then waits at the collective. One innocent dynamic dimension has become a cluster-wide bubble.&lt;/p&gt;

&lt;p&gt;A deep software stack compounds uncertainty. A Python expression triggers graph capture. Graph capture produces guards. Guards select compiled variants. A framework inserts layout conversions. A kernel compiler selects a tile. A driver enqueues work. Firmware manages clocks. The hardware executes. When the result is slow, every layer can truthfully report that its own local action was reasonable. No layer owns the total path.&lt;/p&gt;

&lt;p&gt;The worst failures are not average failures. They are tail failures. One SSD pauses for internal maintenance. One NIC queue receives an incast. One GPU clocks down. One page migrates. One process is descheduled. One rank takes a different fallback graph. Synchronous composition turns the maximum of these delays into the step time. At scale, rare events stop being rare because there are many opportunities for one to occur.&lt;/p&gt;

&lt;p&gt;Leaks and overflows become qualitatively different. A leak in a long-running, terabyte-scale service can consume enough memory to alter NUMA placement long before it crashes. An integer overflow in a byte count can transform a bounds proof into an under-allocation. A queue counter that wraps can make old ownership look new. A tensor extent multiplied in 32 bits can allocate a plausible but wrong buffer. Dynamic systems often discover these errors only when a particular scale is reached in production.&lt;/p&gt;

&lt;p&gt;They did not know the glory of a batch sized exactly right because the right batch is a modern physical shape, not merely a statistical parameter.&lt;/p&gt;

&lt;p&gt;A batch is how software presents work to hardware. It determines whether vector lanes are full, whether tensor-core tiles are complete, whether memory transactions coalesce, whether shared memory is efficiently occupied, whether enough warps exist to hide latency, whether a NIC can amortize doorbells and headers, whether an NVMe queue reaches useful depth, and whether a CPU can keep hot state in cache.&lt;/p&gt;

&lt;p&gt;A batch that is too small pays fixed costs too often. Kernel launches, system calls, queue submissions, packet headers, synchronization edges, and instruction-cache setup dominate. Arithmetic units idle because there is not enough independent work. A batch that is too large creates different failures. It lengthens latency, increases live memory, reduces scheduling flexibility, magnifies a retry, and may cross a capacity boundary that forces a slower layout or fewer resident blocks.&lt;/p&gt;

&lt;p&gt;The perfect batch is not universally largest. It is the batch whose dimensions fit the entire path. A matrix dimension should fit the chosen MMA tile. Token count should fill blocks without excessive masking. A communication shard should align with link striping and ownership. A storage read should align with device and filesystem geometry. The batch should fit memory with enough headroom that no emergency allocator path is reachable. Its duration should be long enough to amortize launch overhead and short enough to preserve pipeline overlap.&lt;/p&gt;

&lt;p&gt;They did not know locks were evil in the hot path because locks are locally simple and globally expensive.&lt;/p&gt;

&lt;p&gt;A lock offers an attractive story: enter, modify shared state, leave. The story hides ownership transfer. On a coherent multiprocessor, the lock’s cache line must move to the core that writes it. Contenders repeatedly read or write that line. The protected data may move too. On NUMA systems, every acquisition can cross a fabric. Fairness can increase handoff traffic. Unfairness can starve. A preempted owner can stop unrelated work. Priority inversion can make urgent work wait for less urgent work. Error paths can forget to release. Multiple locks create order constraints that exist only in programmer discipline.&lt;/p&gt;

&lt;p&gt;Transactions move the difficulty rather than remove it. They need conflict detection, logging or versioning, validation, abort, retry, and a policy for irrevocable effects. Under contention they can turn useful work into repeated discarded work. Distributed locks add leases, clocks, partitions, fencing tokens, and failure recovery. A lock that spans failure domains is a protocol, not a primitive.&lt;/p&gt;

&lt;p&gt;The real alternative is ownership. Partition state so one worker writes it. Give every queue slot one producer and one consumer. Use static shards. Use epochs so readers know which immutable generation is visible. Use double buffers so production and consumption never write the same storage. Use reductions whose communication graph is known in advance. Use credits so capacity is explicit. Make messages idempotent. Make retries safe. Build a DAG in which causality is an edge, not a mutex.&lt;/p&gt;

&lt;p&gt;This does not abolish ordering. A consumer cannot use data before a producer creates it. A GPU block sometimes needs a barrier before reusing shared memory. A device transfer must complete before a dependent kernel reads the destination. The claim is narrower and stronger: necessary causality should be represented directly, while lock-mediated shared mutation should not be the default representation of causality.&lt;/p&gt;

&lt;p&gt;Without loops, recursion, and dynamic sizes, everything becomes butter because the execution graph is finite.&lt;/p&gt;

&lt;p&gt;A source-level loop says that a region may execute an unknown number of times unless a proof elsewhere establishes a bound. A recursive call says that stack depth depends on data unless a proof elsewhere establishes a bound. A dynamic allocation says that memory demand depends on execution unless a proof elsewhere establishes a bound. Every such construct exports an obligation to analysis, testing, or hope.&lt;/p&gt;

&lt;p&gt;Remove them and the obligations collapse into declarations.&lt;/p&gt;

&lt;p&gt;A fixed axis says exactly how many elements exist. A compile-time expansion says exactly how many operation instances exist. A static call graph says exactly how deep control can go. A fixed ownership graph says exactly who can write each region. Resource accounting becomes arithmetic. Bounds checking can happen at compile time. Stack usage is known. Queue capacity is known. DMA descriptors are known. Worst-case work is known. The compiler can inspect every path because there are finitely many paths.&lt;/p&gt;

&lt;p&gt;The important distinction is between source repetition and hardware parallelism. Banning source loops does not mean executing one scalar instruction per element. A named axis can become SIMD lanes, SIMT threads, tensor-core tiles, a reduction tree, a DMA scatter-gather list, or compile-time unrolled instructions. The source describes a finite set. The target chooses the physical mechanism. The program does not need a mutable induction variable to express repetition.&lt;/p&gt;

&lt;p&gt;There is a cost. Blind unrolling can explode code size and destroy the instruction cache. NBL must therefore preserve axes as first-class compile-time sets long enough to lower them intelligently. An axis is not a loop disguised with prettier syntax. It is a proof that the iteration space is finite and named. The backend may implement the set with vector hardware, a GPU grid, a hardware repeat facility, a generated straight-line sequence, or a statically scheduled pipeline. What is forbidden is runtime uncertainty, not efficient repetition by the machine.&lt;/p&gt;

&lt;p&gt;Recursion is especially harmful in systems with deep abstraction stacks. It makes stack consumption data-dependent. It complicates fault containment. It obscures the call graph. It can defeat inlining and whole-program scheduling. Even bounded recursion requires a bound to be carried and checked. An explicit fixed tree or worklist has the same computational meaning while exposing storage and order.&lt;/p&gt;

&lt;p&gt;They did not know how long programs would become.&lt;/p&gt;

&lt;p&gt;Early systems programs were small enough that repetition was visible. A person could understand the memory map, interrupt vectors, scheduler, filesystem, and application. Today a single training run can involve millions of lines across language runtimes, compilers, frameworks, drivers, collective libraries, kernels, firmware, and orchestration. The source the user wrote may be a microscopic fraction of the program the machine executes.&lt;/p&gt;

&lt;p&gt;Length changes the economics of syntax. If every operation repeats its type, shape, device, stride, ownership, and lifetime, the program drowns in duplicated declarations. If those facts are omitted, the program drowns in implicit assumptions. The solution is not shorter names without meaning. It is declaring each fact once and allowing every later line to reuse the fact by identity.&lt;/p&gt;

&lt;p&gt;One dense NBL line should carry a great deal of meaning because the names on that line already carry universal declarations. A tensor contraction can state its output axes and operands; the absent named axis is the reduction. A transfer can name source ownership, destination ownership, and a fixed region; the byte count follows from the shape. A launch can name a kernel whose grid follows from its axes. Density comes from eliminating repeated metadata, not from replacing concepts with punctuation riddles.&lt;/p&gt;

&lt;p&gt;This is the difference between terseness and compression. Terseness removes characters. Compression removes redundancy while retaining information. The goal is almost nothing, not nothing.&lt;/p&gt;

&lt;p&gt;They did not understand how horrible a deep stack would become because every layer was individually reasonable.&lt;/p&gt;

&lt;p&gt;A language runtime made memory safer. A package manager made reuse easier. A tensor framework made autodiff convenient. A graph compiler made kernels faster. A device runtime made launches portable. A collective library made clusters usable. A container made deployment reproducible. An orchestrator made fleets manageable. A service mesh made networking observable. Each layer solved a real problem.&lt;/p&gt;

&lt;p&gt;The horror appears in composition. Every layer owns a partial model of shape, memory, errors, concurrency, and profiling. Objects are wrapped. Errors are translated. Buffers are copied because ownership cannot cross a boundary. Synchronization is inserted because a layer cannot prove what the next layer will do. Metadata is serialized and deserialized. Dynamic dispatch protects generality that the actual workload never uses. Profilers record different clocks and different names. A single operation becomes a pilgrimage through abstractions.&lt;/p&gt;

&lt;p&gt;The call stack is only one stack. There is also the compilation stack, the allocation stack, the network stack, the storage stack, the device stack, the orchestration stack, and the semantic stack. A deep neural network adds literal model layers on top of all of them. Nobody designed the final tower. It accreted.&lt;/p&gt;

&lt;p&gt;Many neural layers intensified the problem. An optimization hidden inside one layer is multiplied by eleven, ninety-six, or a thousand layers. An unnecessary normalization materialization becomes a repeated bandwidth tax. A tiny synchronization becomes a pipeline bubble at every block. Activation lifetime determines whether the model fits. A name such as x becomes meaningless because hundreds of distinct states are all called x in different scopes. The architecture is a long static graph, but the programming system often represents it as dynamic object dispatch inside runtime loops.&lt;/p&gt;

&lt;p&gt;How can it be washed away?&lt;/p&gt;

&lt;p&gt;First, make the whole program visible. CPU code, GPU code, network movement, storage movement, and microcontroller code must be expressible in one source language and one symbol universe. Target-specific instruction streams remain embeddable. The language must not pretend that a GPU kernel and a CPU function share a calling convention when they do not. It should let each target be itself while preserving shared names for the data that crosses targets.&lt;/p&gt;

&lt;p&gt;Second, declare shape once. Every tensor, packet, file, ring, batch, and device shard has named axes with fixed extents. A view may reorder named axes without inventing a new meaning. A contraction identifies axes, not byte offsets. Address arithmetic is a lowering concern. The source should not repeatedly reconstruct row-major offsets from integers.&lt;/p&gt;

&lt;p&gt;Third, declare ownership once. Every writable region has one owner in each phase. Transfers change ownership along explicit edges. Reductions have explicit contributors and destinations. Buffers opened in a scope are closed in that scope. Memory mapped in a scope is unmapped in that scope. A resource cannot leak because there is no path out of the scope that still owns it.&lt;/p&gt;

&lt;p&gt;Fourth, replace runtime loops with finite axes and compile-time expansion. Replace recursion with explicit fixed graphs. Replace dynamic dispatch with a finite family of compiled variants. Replace generic fallback paths with compile-time rejection or an explicitly selected larger case. Keep the iteration space semantic until the backend chooses SIMD, SIMT, MMA, DMA, or straight-line code.&lt;/p&gt;

&lt;p&gt;Fifth, replace locks with partitioning and dataflow. Use one writer per shard. Use fixed queues with static capacity. Use monotonic epochs where a visibility test is unavoidable. Use direct device collectives whose routes are known. Wait only on the edge that represents actual causality. Do not make unrelated work rendezvous at a global barrier.&lt;/p&gt;

&lt;p&gt;Sixth, make profiling part of the program. Every important region has a stable universal name. CPU regions can read architectural counters. GPU kernels can sample device clocks and expose occupancy and memory traffic. Network transfers can record bytes, completions, retries, and queue occupancy. Storage operations can record submission and completion. The profile must preserve the same names through lowering so that source meaning and machine events can be joined.&lt;/p&gt;

&lt;p&gt;Seventh, make failure finite. Every input has a declared maximum. Every protocol message has a fixed layout. Every retry count is bounded and expanded. Every arithmetic width is chosen from the maximum value, not convenience. Every conversion states whether it saturates, traps, or proves range. There is no silent wrap, no hidden allocation, and no unbounded queue.&lt;/p&gt;

&lt;p&gt;Eighth, specialize without apology. A program for eight GPUs is an eight-GPU program. A program for SM90 is an SM90 program. A different machine receives a separately compiled variant. Portability means preserving meaning across backends, not forcing every backend through the least common denominator at runtime.&lt;/p&gt;

&lt;p&gt;The result is not a return to hand-written opcodes. Machine code had truth but not enough retained meaning. Assembly had names but not shapes. C99 had composition but let arrays decay away from their extents. NBL should keep the truth, keep the names, keep the shapes, keep the ownership, and discard the accidental machinery.&lt;/p&gt;

&lt;p&gt;The Arduino blink program demonstrates the smallest version of the idea. The pin is selected because it is connected to a hardware timer output. The timer is configured once. The CPU enters idle sleep. There is no software loop and no interrupt service routine. The peripheral toggles the pin forever. The source does not pretend that repeatedly executing a high-level delay function is the essence of blinking. It identifies the hardware mechanism that already knows how to do the job.&lt;/p&gt;

&lt;p&gt;((oops there goes the tone))&lt;/p&gt;

&lt;p&gt;The FineWeb transport demonstrates the network version. HTTPS is removed because implementing certificate validation, TLS record processing, HTTP redirects, content encodings, and a general URL stack would dominate the actual task. Standard FTP is also more text parsing than the fixed corpus needs. The replacement protocol has one request layout, one reply layout, one exact file size, one checksum, one ownership plan, and a fixed mapping over four NICs and eight storage roots. It is not “nothing.” It is almost nothing, and every remaining piece corresponds to physical work.&lt;/p&gt;

&lt;p&gt;The tokenizer demonstrates why static axes matter. UTF-8 validity, Unicode class lookup, GPT-2 pretoken boundaries, BPE pair lookup, merge selection, token compaction, shard placement, and file durability are explicit phases. The Unicode and merge tables are included data, not a hidden package download. Prefix operations are fixed doubling networks. Binary searches have a fixed number of comparisons. The worst case exists in the source.&lt;/p&gt;

&lt;p&gt;The FlashAttention translations preserve the exact architecture and tile choices from the attached launch code. The forward combine keeps split count, head count, row count, and dimension as fixed maxima with active masks. The backward program keeps preprocess, main computation, GQA accumulation, postprocess, causal/local/softcap choices, and the SM80/SM86/SM89/SM90 tuning family. No C++ template system is needed at runtime because every variant is already a named finite program.&lt;/p&gt;

&lt;p&gt;The GPT training translation fixes the world at eight ranks because the source’s most specialized sparse path is built around eight. It fixes model dimensions, layer topology, parameter-bank padding, schedule stages, communication ownership, optimizer order, and all 1390 training steps. The eleven model layers are named and expanded rather than traversed by a runtime loop. The five Polar Express iterations are five explicit transformations. Adam’s odd-step policy, cautious weight decay, mantissa retention, sparse bigram exchange, reduce-scatter, all-gather, YaRN transitions, validation points, and final window extension are represented as finite graph edges.&lt;/p&gt;

&lt;p&gt;Perfection is not a slogan that can substitute for measurement. A target schedule is accepted only after counters show that it reaches the intended arithmetic, bandwidth, occupancy, and interconnect limits. A handwritten SASS sequence can beat PTX on one exact GPU and lose on a stepping with different scheduling constraints. A static program makes this process tractable because every run of one variant is comparable. It does not make benchmarking unnecessary; it makes benchmarking meaningful.&lt;/p&gt;

&lt;p&gt;That is how the accidents are washed away: not by assuming the machine is simple, but by refusing to hide its complexity behind dynamic generality. The program becomes small because the machine plan is exact. It becomes dense because every name keeps its meaning. It becomes fast because data movement, batch shape, ownership, and topology are decided before execution. It becomes provable because the graph is finite. It becomes profilable because the names survive. It becomes almost nothing because everything that remains earns its place.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to serve deepseek r1 &amp; v3 on GH200s (400 tok/sec throughput, 10 tok/sec/query)</title>
      <dc:creator>Luke Miles</dc:creator>
      <pubDate>Tue, 11 Feb 2025 03:52:30 +0000</pubDate>
      <link>https://dev.to/qpwo/how-to-serve-deepseek-r1-v3-on-gh200s-300-toksec-throughput-10-toksecquery-1d</link>
      <guid>https://dev.to/qpwo/how-to-serve-deepseek-r1-v3-on-gh200s-300-toksec-throughput-10-toksecquery-1d</guid>
      <description>&lt;p&gt;Lambda labs still has half-off (ie $1.50) GH200s as a promotion to get more people used to the ARM tooling. I &lt;a href="https://dev.to/qpwo/how-to-run-llama-405b-bf16-with-gh200s-7da"&gt;previously&lt;/a&gt; wrote a tutorial for running llama 405b, but deepseek r1 is clearly a better model, so let's serve it instead. There's a couple differences from serving llama:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need 12 or 16 GPUs to have decent throughput. You can still use 8 if you don't need much throughput.&lt;/li&gt;
&lt;li&gt;VLLM works better than aphrodite for deepseek right now. In fact they just released an &lt;a href="https://github.com/vllm-project/vllm/releases/tag/v0.7.2" rel="noopener noreferrer"&gt;update&lt;/a&gt; that makes deepseek inference quite a bit faster. Roughly a 40% throughput improvement.

&lt;ul&gt;
&lt;li&gt;Normally a 16x 1x gpu cluster would be pretty slow for inference but for some reason this actually works pretty well.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Guide will be updated &lt;a href="https://github.com/qpwo/deepseek-gh200-tutorial" rel="noopener noreferrer"&gt;on the github repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Video showing inference speed with 64 parallel queries. It prints once per second and you get at least 5 new words every time it prints:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/user-attachments/assets/5c27abe9-9168-4f6a-88aa-8ffa22c348b1" rel="noopener noreferrer"&gt;https://github.com/user-attachments/assets/5c27abe9-9168-4f6a-88aa-8ffa22c348b1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create instances
&lt;/h2&gt;

&lt;p&gt;This time, instead of using the website, let's use the API to make the instances.&lt;/p&gt;

&lt;p&gt;Make a &lt;a href="https://cloud.lambdalabs.com/file-systems" rel="noopener noreferrer"&gt;filesystem&lt;/a&gt; called 'shared' in Washington, DC and write down the name of your &lt;a href="https://cloud.lambdalabs.com/ssh-keys" rel="noopener noreferrer"&gt;ssh key&lt;/a&gt;. Also generate an &lt;a href="https://cloud.lambdalabs.com/api-keys" rel="noopener noreferrer"&gt;api key&lt;/a&gt; and save it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;sshkey_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-key
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;shared_fs_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;shared
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;LAMBDA_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script will make 16 GH200 instances. Ignore the rate limit errors, it will keep retrying until 16 have been made.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;function &lt;/span&gt;lambda-api &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;route&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
    &lt;span class="nb"&gt;shift &lt;/span&gt;2
    curl &lt;span class="nt"&gt;--fail&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$LAMBDA_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="s2"&gt;"https://cloud.lambdalabs.com/api/v1/&lt;/span&gt;&lt;span class="nv"&gt;$route&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; lambda-api

&lt;span class="nv"&gt;num_want&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;16
&lt;span class="nv"&gt;num_got&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$num_got&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; &lt;span class="nv"&gt;$num_want&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;lambda-api POST instance-operations/launch &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
        "region_name": "us-east-3",
        "instance_type_name": "gpu_1x_gh200",
        "sshkey_names": ["'&lt;/span&gt;&lt;span class="nv"&gt;$sshkey_name&lt;/span&gt;&lt;span class="s1"&gt;'"],
        "file_system_names": ["'&lt;/span&gt;&lt;span class="nv"&gt;$shared_fs_name&lt;/span&gt;&lt;span class="s1"&gt;'"],
        "quantity": 1,
        "name": "node_'&lt;/span&gt;&lt;span class="nv"&gt;$num_got&lt;/span&gt;&lt;span class="s1"&gt;'"
    }'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;num_got++&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="nb"&gt;echo &lt;/span&gt;&lt;span class="nv"&gt;num_got&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$num_got&lt;/span&gt;
    &lt;span class="nb"&gt;sleep &lt;/span&gt;3
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After all the instances are created, copy all the IP addresses from the &lt;a href="https://cloud.lambdalabs.com/instances" rel="noopener noreferrer"&gt;instances page&lt;/a&gt; and save it to &lt;code&gt;~/ips.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6okkog8mutobdjc7aopv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6okkog8mutobdjc7aopv.png" alt="Image description" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bulk ssh connection helpers
&lt;/h2&gt;

&lt;p&gt;I prefer direct bash &amp;amp; ssh over anything fancy like kubernetes or slurm. It's manageable with some helpers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# save all the ssh fingerprints now to skip confirmation later&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;ip &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/ips.txt&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"doing &lt;/span&gt;&lt;span class="nv"&gt;$ip&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    ssh-keyscan &lt;span class="nv"&gt;$ip&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.ssh/known_hosts
&lt;span class="k"&gt;done

&lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;runprefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;runip&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/lambda_id_ed25519 ubuntu@&lt;span class="nv"&gt;$ip&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nb"&gt;stdbuf&lt;/span&gt; &lt;span class="nt"&gt;-oL&lt;/span&gt; &lt;span class="nt"&gt;-eL&lt;/span&gt; bash &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%q"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$runprefix&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &amp;lt; /dev/null
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;runk&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;k &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;p ~/ips.txt&lt;span class="si"&gt;)&lt;/span&gt; runip &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;runhead&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt; ~/ips.txt&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; runip &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;runips&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;pids&lt;/span&gt;&lt;span class="o"&gt;=()&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;ip &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$ips&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
        &lt;/span&gt;&lt;span class="nv"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$ip&lt;/span&gt; runip &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; |&amp;amp; &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/^/&lt;/span&gt;&lt;span class="nv"&gt;$ip&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt; /"&lt;/span&gt; &amp;amp;
        pids+&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="nv"&gt;$!&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;done
    &lt;/span&gt;&lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;pids&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &amp;amp;&amp;gt;/dev/null
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;runall&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;ips&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/ips.txt&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; runips &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;runrest&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;ips&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt;+2 ~/ips.txt&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; runips &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;sshk&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;k &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;p ~/ips.txt&lt;span class="si"&gt;)&lt;/span&gt;
    ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/lambda_id_ed25519 ubuntu@&lt;span class="nv"&gt;$ip&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;ssh_head&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'k=0 sshk'&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;killall&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    pkill &lt;span class="nt"&gt;-ife&lt;/span&gt; 192.222
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's check that it works&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;runall &lt;span class="nb"&gt;echo &lt;/span&gt;ok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Set up NFS cache
&lt;/h2&gt;

&lt;p&gt;We'll be putting the python environment in the NFS. It will load much faster if we cache it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# First, check the NFS works.&lt;/span&gt;
&lt;span class="c"&gt;# runall ln -s my_other_fs_name shared&lt;/span&gt;
runhead &lt;span class="s1"&gt;'echo world &amp;gt; shared/hello'&lt;/span&gt;
runall &lt;span class="nb"&gt;cat &lt;/span&gt;shared/hello


&lt;span class="c"&gt;# Install and enable cachefilesd&lt;/span&gt;
runall &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
runall &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; cachefilesd
runall &lt;span class="s2"&gt;"echo '
RUN=yes
CACHE_TAG=mycache
CACHE_BACKEND=Path=/var/cache/fscache
CACHEFS_RECLAIM=0
' | sudo tee -a /etc/default/cachefilesd"&lt;/span&gt;
runall &lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart cachefilesd
runall &lt;span class="s1"&gt;'sudo journalctl -u cachefilesd | tail -n2'&lt;/span&gt;

&lt;span class="c"&gt;# Set the "fsc" option on the NFS mount&lt;/span&gt;
runhead &lt;span class="nb"&gt;cat&lt;/span&gt; /etc/fstab &lt;span class="c"&gt;# should have mount to ~/shared&lt;/span&gt;
runall &lt;span class="nb"&gt;cp&lt;/span&gt; /etc/fstab etc-fstab-bak.txt
runall &lt;span class="nb"&gt;sudo sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/,proto=tcp,/,proto=tcp,fsc,/g'&lt;/span&gt; /etc/fstab
runall &lt;span class="nb"&gt;cat&lt;/span&gt; /etc/fstab

&lt;span class="c"&gt;# Remount&lt;/span&gt;
runall &lt;span class="nb"&gt;sudo &lt;/span&gt;umount /home/ubuntu/shared
runall &lt;span class="nb"&gt;sudo &lt;/span&gt;mount /home/ubuntu/shared
runall &lt;span class="nb"&gt;cat&lt;/span&gt; /proc/fs/nfsfs/volumes &lt;span class="c"&gt;# FSC column should say "yes"&lt;/span&gt;

&lt;span class="c"&gt;# Test cache speedup&lt;/span&gt;
runhead &lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/urandom &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;shared/bigfile &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8192
runall &lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;shared/bigfile &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/null &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M &lt;span class="c"&gt;# First one takes 8 seconds&lt;/span&gt;
runall &lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;shared/bigfile &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/null &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M &lt;span class="c"&gt;# Seond takes 0.6 seconds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install python 3.11
&lt;/h2&gt;

&lt;p&gt;I've had better luck with &lt;code&gt;apt&lt;/code&gt; packages than &lt;code&gt;conda&lt;/code&gt; ones, so I'll use &lt;code&gt;apt&lt;/code&gt; in this tutorial.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;runall &lt;span class="nb"&gt;sudo &lt;/span&gt;add-apt-repository ppa:deadsnakes/ppa &lt;span class="nt"&gt;-y&lt;/span&gt;
runall &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    python3.11 python3.11-dev python3.11-venv python3.11-distutils
runall python3.11 &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create virtualenv
&lt;/h2&gt;

&lt;p&gt;Instead of carefully doing the exact same commands on every machine, we can use a virtual environment in the NFS and just control it with the head node. This makes it a lot easier to correct mistakes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;runhead python3.11 &lt;span class="nt"&gt;-m&lt;/span&gt; venv &lt;span class="nt"&gt;--copies&lt;/span&gt; shared/myvenv
runhead &lt;span class="s1"&gt;'source shared/myvenv/bin/activate; which python'&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;runprefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'source shared/myvenv/bin/activate ; '&lt;/span&gt; &lt;span class="c"&gt;# this is used by runip()&lt;/span&gt;
runall which python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Download models
&lt;/h2&gt;

&lt;p&gt;I hit some kind of networking issue when I used more workers, but you can download r1 and v3 in about 20 minutes each this way. You can let this run in the background while you do the next step in a separate terminal. I think you get faster downloads if you use a &lt;a href="https://huggingface.co/settings/tokens" rel="noopener noreferrer"&gt;huggingface token&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;runhead &lt;span class="s2"&gt;"pip install hf_transfer 'huggingface_hub[hf_transfer]'"&lt;/span&gt;
runall &lt;span class="s2"&gt;"huggingface-cli login --token ..."&lt;/span&gt;
runall &lt;span class="s2"&gt;"export HF_HUB_ENABLE_HF_TRANSFER=1
    huggingface-cli download --max-workers=1 --local-dir ~/dsr1 deepseek-ai/DeepSeek-R1
    huggingface-cli download --max-workers=1 --local-dir ~/dsv3 deepseek-ai/DeepSeek-V3"&lt;/span&gt;
&lt;span class="c"&gt;# if you need to restart:&lt;/span&gt;
pkill &lt;span class="nt"&gt;-ife&lt;/span&gt; 192.222
runall pkill &lt;span class="nt"&gt;-ife&lt;/span&gt; hugg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I spent a few hours writing a script to have each server download a part of the model, then synchronize it between all servers, but it wasn't actually much faster. Just download straight from hf.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install VLLM
&lt;/h2&gt;

&lt;p&gt;You can build from source if you prefer, or use my wheels. Either way you'll need pytorch first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;runhead &lt;span class="s2"&gt;"pip install --force-reinstall &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    'numpy&amp;lt;2' torch==2.5.1 torchvision &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    --index-url 'https://download.pytorch.org/whl/cu124'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  install everything from wheels
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s1"&gt;'https://github.com/qpwo/deepseek-gh200-tutorial/releases/download/v0/triton-3.1.0-cp311-cp311-linux_aarch64.whl'&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s1"&gt;'https://github.com/qpwo/deepseek-gh200-tutorial/releases/download/v0/vllm-0.7.2+cu124-cp311-cp311-linux_aarch64.whl'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  triton from source
&lt;/h3&gt;

&lt;p&gt;Don't waste your time trying to configure parallel builds, ninja defaults are basically optimal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;runhead git clone https://github.com/triton-lang/triton.git ~/git/triton
runhead &lt;span class="s2"&gt;"cd ~/git/triton &amp;amp;&amp;amp; git checkout release/3.1.x"&lt;/span&gt;
&lt;span class="c"&gt;# about 5 minutes:&lt;/span&gt;
runhead &lt;span class="s2"&gt;"cd ~/git/triton &amp;amp;&amp;amp; python -m build --no-isolation --wheel --verbose ./python"&lt;/span&gt;
runhead &lt;span class="s2"&gt;"cd ~/git/triton &amp;amp;&amp;amp; pip install -v python/dist/*.whl"&lt;/span&gt;

&lt;span class="c"&gt;# It's a good idea to save the wheel for next time.&lt;/span&gt;
scp &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/lambda_id_ed25519 ubuntu@&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt; ~/ips.txt&lt;span class="si"&gt;)&lt;/span&gt;:/home/ubuntu/git/triton/python/dist/&lt;span class="k"&gt;*&lt;/span&gt;.whl ~/Downloads/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  VLLM from source
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;runhead pip &lt;span class="nb"&gt;install &lt;/span&gt;ninja setuptools build setuptools_scm wheel bindings build cmake
runhead git clone https://github.com/vllm-project/vllm ~/git/vllm
runhead &lt;span class="s1"&gt;'cd ~/git/vllm &amp;amp;&amp;amp; git checkout v0.7.2'&lt;/span&gt;
&lt;span class="c"&gt;# this will take about 20 minutes:&lt;/span&gt;
runhead &lt;span class="s1"&gt;'cd ~/git/vllm &amp;amp;&amp;amp; python -m build --no-isolation --wheel --verbose .'&lt;/span&gt;
runhead &lt;span class="s1"&gt;'pip install -v ~/git/vllm/dist/*.whl'&lt;/span&gt;
&lt;span class="c"&gt;# ((You may have to run torch install command again))&lt;/span&gt;

&lt;span class="c"&gt;# save the wheel for next time:&lt;/span&gt;
scp &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/lambda_id_ed25519 ubuntu@&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt; ~/ips.txt&lt;span class="si"&gt;)&lt;/span&gt;:/home/ubuntu/git/vllm/dist/&lt;span class="k"&gt;*&lt;/span&gt;.whl ~/Downloads/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check install
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;runall &lt;span class="s2"&gt;"python -c 'import triton; print(&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;triton ok&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;)'"&lt;/span&gt;
runall &lt;span class="s2"&gt;"python -c 'import torch; print(torch.tensor(2).cuda() + 2)'"&lt;/span&gt;
runall &lt;span class="s2"&gt;"vllm --help | tail -n1"&lt;/span&gt; &lt;span class="c"&gt;# slow first time, fast second time&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Serve it!
&lt;/h2&gt;

&lt;p&gt;The nodes communicate using &lt;a href="https://docs.ray.io/en/latest/index.html" rel="noopener noreferrer"&gt;ray&lt;/a&gt;, so first we'll start a ray cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;runall &lt;span class="s2"&gt;"ray stop; ray stop; pkill -ife py"&lt;/span&gt;
runall &lt;span class="s1"&gt;'nvidia-smi --query-gpu=index,power.draw,memory.used,memory.free --format=csv,noheader'&lt;/span&gt;
&lt;span class="c"&gt;# This will give you the private/LAN IP address you use to connect the other nodes:&lt;/span&gt;
runhead ray start &lt;span class="nt"&gt;--head&lt;/span&gt;
&lt;span class="c"&gt;# Use the output from above:&lt;/span&gt;
runrest ray start &lt;span class="nt"&gt;--address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'...'&lt;/span&gt;
runall &lt;span class="s2"&gt;"ray status | grep GPU"&lt;/span&gt; &lt;span class="c"&gt;# should see "0.0/16.0 GPU" 16 times&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now we can fire up vllm. The first run will cause weights to be cached in RAM, so the second time you start vllm will be faster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 2-8 minutes to start:&lt;/span&gt;
&lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dsr1
&lt;span class="c"&gt;# model=dsv3&lt;/span&gt;
runhead &lt;span class="s2"&gt;"vllm serve ~/&lt;/span&gt;&lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    --api-key asdf1234 &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    --served-model-name &lt;/span&gt;&lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    --trust-remote-code &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    --pipeline-parallel-size=4 &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    --tensor-parallel-size=4 &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    --enable-prefix-caching &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    --uvicorn-log-level=info &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    --max-num-seqs=64 &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    --max-model-len=8000"&lt;/span&gt;
&lt;span class="c"&gt;# wait until you see:&lt;/span&gt;
&lt;span class="c"&gt;# INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Load testing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;edit&lt;/strong&gt; browser-based thing seems to self-limit, so use load-test.py instead&lt;/p&gt;

&lt;p&gt;I made a little browser thing where you can load-test the server. How to run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-L&lt;/span&gt; 1234:localhost:1234 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-L&lt;/span&gt; 8000:localhost:8000 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-L&lt;/span&gt; 8265:localhost:8265 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/lambda_id_ed25519 &lt;span class="se"&gt;\&lt;/span&gt;
    ubuntu@&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt; ~/ips.txt&lt;span class="si"&gt;)&lt;/span&gt;
git clone https://github.com/qpwo/deepseek-gh200-tutorial ~/git/tut
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/git/tut
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; http.server 1234

&lt;span class="c"&gt;# on your desktop:&lt;/span&gt;
open http://localhost:1234/load-test.html

&lt;span class="c"&gt;# check if api is up:&lt;/span&gt;
curl http://localhost:8000/v1/models &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer asdf1234"&lt;/span&gt;

&lt;span class="c"&gt;# ray dashboard to view VRAM utilization:&lt;/span&gt;
open http://localhost:8265/#/cluster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>How to run llama 405b bf16 with gh200s</title>
      <dc:creator>Luke Miles</dc:creator>
      <pubDate>Sat, 21 Dec 2024 19:20:14 +0000</pubDate>
      <link>https://dev.to/qpwo/how-to-run-llama-405b-bf16-with-gh200s-7da</link>
      <guid>https://dev.to/qpwo/how-to-run-llama-405b-bf16-with-gh200s-7da</guid>
      <description>&lt;p&gt;Lambda labs has half-off GH200s right now to get more people used to the ARM tooling. This means you can maybe actually afford to run the biggest open-source models! The only caveat is that you'll have to occasionally build something from source. Here's how I got llama 405b running with full precision on the GH200s.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create instances
&lt;/h3&gt;

&lt;p&gt;Llama 405b is about 750GB so you want about 10 96GB GPUS to run it. (The GH200 has pretty good CPU-GPU memory swapping speed -- that's kind of the whole point of the GH200 -- so you can use as few as 3. Time-per-token will be terrible, but total throughput is acceptable, if you're doing batch-processing.) Sign in to lambda labs and create a bunch of GH200 instances. &lt;strong&gt;Make sure to give them all the same shared network filesystem.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjhtn33f1abnha6ng50c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjhtn33f1abnha6ng50c.png" alt="lambda labs screenshot" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save the ip addresses to ~/ips.txt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bulk ssh connection helpers
&lt;/h3&gt;

&lt;p&gt;I prefer direct bash &amp;amp; ssh over anything fancy like kubernetes or slurm. It's manageable with some helpers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# skip fingerprint confirmation&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;ip &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/ips.txt&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"doing &lt;/span&gt;&lt;span class="nv"&gt;$ip&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    ssh-keyscan &lt;span class="nv"&gt;$ip&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.ssh/known_hosts
&lt;span class="k"&gt;done

function &lt;/span&gt;run_ip&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/lambda_id_ed25519 ubuntu@&lt;span class="nv"&gt;$ip&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nb"&gt;stdbuf&lt;/span&gt; &lt;span class="nt"&gt;-oL&lt;/span&gt; &lt;span class="nt"&gt;-eL&lt;/span&gt; bash &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%q"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &amp;lt; /dev/null
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;run_k&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$k&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;p ~/ips.txt&lt;span class="si"&gt;)&lt;/span&gt; run_ip &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;runhead&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt; ~/ips.txt&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; run_ip &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;run_ips&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;ip &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$ips&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
        &lt;/span&gt;&lt;span class="nv"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$ip&lt;/span&gt; run_ip &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; |&amp;amp; &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/^/&lt;/span&gt;&lt;span class="nv"&gt;$ip&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt; /"&lt;/span&gt; &amp;amp;
        &lt;span class="c"&gt;# pids="$pids $!"&lt;/span&gt;
    &lt;span class="k"&gt;done
    &lt;/span&gt;&lt;span class="nb"&gt;wait&lt;/span&gt; &amp;amp;&amp;gt; /dev/null
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;runall&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;ips&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/ips.txt&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; run_ips &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;runrest&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;ips&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt;+2 ~/ips.txt&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; run_ips &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;ssh_k&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$k&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;p ~/ips.txt&lt;span class="si"&gt;)&lt;/span&gt;
    ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/lambda_id_ed25519 ubuntu@&lt;span class="nv"&gt;$ip&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;ssh_head&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'k=1 ssh_k'&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;killall&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    pkill &lt;span class="nt"&gt;-ife&lt;/span&gt; &lt;span class="s1"&gt;'.ssh/lambda_id_ed25519'&lt;/span&gt;
    &lt;span class="nb"&gt;sleep &lt;/span&gt;1
    pkill &lt;span class="nt"&gt;-ife&lt;/span&gt; &lt;span class="nt"&gt;-9&lt;/span&gt; &lt;span class="s1"&gt;'.ssh/lambda_id_ed25519'&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;jobs&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;&lt;span class="nb"&gt;fg&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Set up NFS cache
&lt;/h3&gt;

&lt;p&gt;We'll be putting the python environment and the model weights in the NFS. It will load much faster if we cache it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# First, check the NFS works.&lt;/span&gt;
&lt;span class="c"&gt;# runall ln -s my_other_fs_name shared&lt;/span&gt;
runhead &lt;span class="s1"&gt;'echo world &amp;gt; shared/hello'&lt;/span&gt;
runall &lt;span class="nb"&gt;cat &lt;/span&gt;shared/hello

&lt;span class="c"&gt;# Install and enable cachefilesd&lt;/span&gt;
runall &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
runall &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; cachefilesd
runall &lt;span class="s2"&gt;"echo '
RUN=yes
CACHE_TAG=mycache
CACHE_BACKEND=Path=/var/cache/fscache
CACHEFS_RECLAIM=0
' | sudo tee -a /etc/default/cachefilesd"&lt;/span&gt;
runall &lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart cachefilesd
runall &lt;span class="s1"&gt;'sudo journalctl -u cachefilesd | tail -n2'&lt;/span&gt;

&lt;span class="c"&gt;# Set the "fsc" option on the NFS mount&lt;/span&gt;
runhead &lt;span class="nb"&gt;cat&lt;/span&gt; /etc/fstab &lt;span class="c"&gt;# should have mount to ~/shared&lt;/span&gt;
runall &lt;span class="nb"&gt;cp&lt;/span&gt; /etc/fstab etc-fstab-bak.txt
runall &lt;span class="nb"&gt;sudo sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/,proto=tcp,/,proto=tcp,fsc,/g'&lt;/span&gt; /etc/fstab
runall &lt;span class="nb"&gt;cat&lt;/span&gt; /etc/fstab

&lt;span class="c"&gt;# Remount&lt;/span&gt;
runall &lt;span class="nb"&gt;sudo &lt;/span&gt;umount /home/ubuntu/wash2
runall &lt;span class="nb"&gt;sudo &lt;/span&gt;mount /home/ubuntu/wash2
runall &lt;span class="nb"&gt;cat&lt;/span&gt; /proc/fs/nfsfs/volumes &lt;span class="c"&gt;# FSC column should say "yes"&lt;/span&gt;

&lt;span class="c"&gt;# Test cache speedup&lt;/span&gt;
runhead &lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/urandom &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;shared/bigfile &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8192
runall &lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;shared/bigfile &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/null &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M &lt;span class="c"&gt;# First one takes 8 seconds&lt;/span&gt;
runall &lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;shared/bigfile &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/null &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M &lt;span class="c"&gt;# Seond takes 0.6 seconds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create conda environment
&lt;/h3&gt;

&lt;p&gt;Instead of carefully doing the exact same commands on every machine, we can use a conda environment in the NFS and just control it with the head node.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# We'll also use a shared script instead of changing ~/.profile directly.&lt;/span&gt;
&lt;span class="c"&gt;# Easier to fix mistakes that way.&lt;/span&gt;
runhead &lt;span class="s1"&gt;'echo ". /opt/miniconda/etc/profile.d/conda.sh" &amp;gt;&amp;gt; shared/common.sh'&lt;/span&gt;
runall &lt;span class="s1"&gt;'echo "source /home/ubuntu/shared/common.sh" &amp;gt;&amp;gt; ~/.profile'&lt;/span&gt;
runall which conda

&lt;span class="c"&gt;# Create the environment&lt;/span&gt;
runhead &lt;span class="s1"&gt;'conda create --prefix ~/shared/311 -y python=3.11'&lt;/span&gt;
runhead &lt;span class="s1"&gt;'~/shared/311/bin/python --version'&lt;/span&gt; &lt;span class="c"&gt;# double-check that it is executable&lt;/span&gt;
runhead &lt;span class="s1"&gt;'echo "conda activate ~/shared/311" &amp;gt;&amp;gt; shared/common.sh'&lt;/span&gt;
runall which python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install aphrodite dependencies
&lt;/h3&gt;

&lt;p&gt;Aphrodite is a fork of vllm that starts a bit quicker and has some extra features.&lt;br&gt;
It will run the openai-compatible inference API and the model itself.&lt;/p&gt;

&lt;p&gt;You need torch, triton, and flash-attention.&lt;br&gt;
You can get aarch64 torch builds from pytorch.org (you do not want to build it yourself).&lt;br&gt;
The other two you can either build yourself or use the wheel I made.&lt;/p&gt;

&lt;p&gt;If you build from source, then you can save a bit of time by running the &lt;code&gt;python setup.py bdist_wheel&lt;/code&gt; for triton, flash-attention, and aphrodite in parallel on three different machines. Or  you can do them one-by-one on the same machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;runhead pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s1"&gt;'numpy&amp;lt;2'&lt;/span&gt; &lt;span class="nv"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;2.4.0 &lt;span class="nt"&gt;--index-url&lt;/span&gt; &lt;span class="s1"&gt;'https://download.pytorch.org/whl/cu124'&lt;/span&gt;

&lt;span class="c"&gt;# fix for "libstdc++.so.6: version `GLIBCXX_3.4.30' not found" error:&lt;/span&gt;
runhead conda &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; conda-forge libstdcxx-ng&lt;span class="o"&gt;=&lt;/span&gt;12

runhead python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'import torch; print(torch.tensor(2).cuda() + 2, "torch ok")'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  triton &amp;amp; flash attention from wheels
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;runhead pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s1"&gt;'https://github.com/qpwo/lambda-gh200-llama-405b-tutorial/releases/download/v0.1/triton-3.2.0+git755d4164-cp311-cp311-linux_aarch64.whl'&lt;/span&gt;
runhead pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s1"&gt;'https://github.com/qpwo/lambda-gh200-llama-405b-tutorial/releases/download/v0.1/aphrodite_flash_attn-2.6.1.post2-cp311-cp311-linux_aarch64.whl'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  triton from source
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 ssh_k &lt;span class="c"&gt;# ssh into first machine&lt;/span&gt;

pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-U&lt;/span&gt; pip setuptools wheel ninja cmake setuptools_scm
git config &lt;span class="nt"&gt;--global&lt;/span&gt; feature.manyFiles &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="c"&gt;# faster clones&lt;/span&gt;
git clone https://github.com/triton-lang/triton.git ~/shared/triton
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/shared/triton/python
git checkout 755d4164 &lt;span class="c"&gt;# &amp;lt;-- optional, tested versions&lt;/span&gt;
&lt;span class="c"&gt;# Note that ninja already parallelizes everything to the extent possible,&lt;/span&gt;
&lt;span class="c"&gt;# so no sense trying to change the cmake flags or anything.&lt;/span&gt;
python setup.py bdist_wheel
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-deps&lt;/span&gt; dist/&lt;span class="k"&gt;*&lt;/span&gt;.whl &lt;span class="c"&gt;# good idea to download this too for later&lt;/span&gt;
python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'import triton; print("triton ok")'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  flash-attention from source
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 ssh_k &lt;span class="c"&gt;# go into second machine&lt;/span&gt;

git clone https://github.com/AlpinDale/flash-attention  ~/shared/flash-attention
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/shared/flash-attention
python setup.py bdist_wheel
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-deps&lt;/span&gt; dist/&lt;span class="k"&gt;*&lt;/span&gt;.whl
python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'import aphrodite_flash_attn; import aphrodite_flash_attn_2_cuda; print("flash attn ok")'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install aphrodite
&lt;/h3&gt;

&lt;p&gt;You can use my wheel or build it yourself.&lt;/p&gt;

&lt;h4&gt;
  
  
  aphrodite from wheel
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;runhead pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s1"&gt;'https://github.com/qpwo/lambda-gh200-llama-405b-tutorial/releases/download/v0.1/aphrodite_engine-0.6.4.post1-cp311-cp311-linux_aarch64.whl'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  aphrodite from source
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 ssh_k &lt;span class="c"&gt;# building this on the third machine&lt;/span&gt;

git clone https://github.com/PygmalionAI/aphrodite-engine.git ~/shared/aphrodite-engine
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/shared/aphrodite-engine
pip &lt;span class="nb"&gt;install &lt;/span&gt;&lt;span class="nv"&gt;protobuf&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;3.20.2 ninja msgspec coloredlogs portalocker pytimeparse  &lt;span class="nt"&gt;-r&lt;/span&gt; requirements-common.txt
python setup.py bdist_wheel
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-deps&lt;/span&gt; dist/&lt;span class="k"&gt;*&lt;/span&gt;.whl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check all installs succeeded
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;function &lt;/span&gt;runallpyc&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; runall &lt;span class="s2"&gt;"python -c &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; %q &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
runallpyc &lt;span class="s1"&gt;'import torch; print(torch.tensor(5).cuda() + 1, "torch ok")'&lt;/span&gt;
runallpyc &lt;span class="s1"&gt;'import triton; print("triton ok")'&lt;/span&gt;
runallpyc &lt;span class="s1"&gt;'import aphrodite_flash_attn; import aphrodite_flash_attn_2_cuda; print("flash attn ok")'&lt;/span&gt;
runallpyc &lt;span class="s1"&gt;'import aphrodite; print("aphrodite ok")'&lt;/span&gt;
runall &lt;span class="s1"&gt;'aphrodite run --help | head -n1'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Download the weights
&lt;/h3&gt;

&lt;p&gt;Go to &lt;a href="https://huggingface.co/meta-llama/Llama-3.1-405B-Instruct" rel="noopener noreferrer"&gt;https://huggingface.co/meta-llama/Llama-3.1-405B-Instruct&lt;/a&gt; and make sure you have the right permissions. The approval usually takes about an hour. Get a token from &lt;a href="https://huggingface.co/settings/tokens" rel="noopener noreferrer"&gt;https://huggingface.co/settings/tokens&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;hf_transfer &lt;span class="s1"&gt;'huggingface_hub[hf_transfer]'&lt;/span&gt;

runall git config &lt;span class="nt"&gt;--global&lt;/span&gt; credential.helper store
runall huggingface-cli login &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="nv"&gt;$new_hf&lt;/span&gt;

&lt;span class="c"&gt;# this tells the huggingface-cli to use the fancy beta downloader&lt;/span&gt;
runhead &lt;span class="s2"&gt;"echo 'export HF_HUB_ENABLE_HF_TRANSFER=1' &amp;gt;&amp;gt; ~/shared/common.sh"&lt;/span&gt;
runall &lt;span class="s1"&gt;'echo $HF_HUB_ENABLE_HF_TRANSFER'&lt;/span&gt;

runall pkill &lt;span class="nt"&gt;-ife&lt;/span&gt; huggingface-cli &lt;span class="c"&gt;# kill any stragglers&lt;/span&gt;

&lt;span class="c"&gt;# we can speed up the model download by having each server download part&lt;/span&gt;
&lt;span class="nv"&gt;local_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/ubuntu/shared/hff/405b-instruct
&lt;span class="nv"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 run_k huggingface-cli download &lt;span class="nt"&gt;--max-workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;32 &lt;span class="nt"&gt;--revision&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"model-000[0-4]?-of-00191.safetensors"&lt;/span&gt; &lt;span class="nt"&gt;--local-dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$local_dir&lt;/span&gt; meta-llama/Meta-Llama-3.1-405B-Instruct &amp;amp;
&lt;span class="nv"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 run_k huggingface-cli download &lt;span class="nt"&gt;--max-workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;32 &lt;span class="nt"&gt;--revision&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;  &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"model-000[5-9]?-of-00191.safetensors"&lt;/span&gt; &lt;span class="nt"&gt;--local-dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$local_dir&lt;/span&gt; meta-llama/Meta-Llama-3.1-405B-Instruct &amp;amp;
&lt;span class="nv"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 run_k huggingface-cli download &lt;span class="nt"&gt;--max-workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;32 &lt;span class="nt"&gt;--revision&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;  &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"model-001[0-4]?-of-00191.safetensors"&lt;/span&gt; &lt;span class="nt"&gt;--local-dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$local_dir&lt;/span&gt; meta-llama/Meta-Llama-3.1-405B-Instruct &amp;amp;
&lt;span class="nv"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4 run_k huggingface-cli download &lt;span class="nt"&gt;--max-workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;32 &lt;span class="nt"&gt;--revision&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;  &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"model-001[5-9]?-of-00191.safetensors"&lt;/span&gt; &lt;span class="nt"&gt;--local-dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$local_dir&lt;/span&gt; meta-llama/Meta-Llama-3.1-405B-Instruct &amp;amp;

&lt;span class="nb"&gt;wait&lt;/span&gt;
&lt;span class="c"&gt;# download misc remaining files&lt;/span&gt;
&lt;span class="nv"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 run_k huggingface-cli download &lt;span class="nt"&gt;--max-workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;32 &lt;span class="nt"&gt;--revision&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="nt"&gt;--exclude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'*.pth'&lt;/span&gt; &lt;span class="nt"&gt;--local-dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$local_dir&lt;/span&gt; meta-llama/Meta-Llama-3.1-405B-Instruct
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  run llama 405b
&lt;/h3&gt;

&lt;p&gt;We'll make the servers aware of each other by starting &lt;code&gt;ray&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;runhead pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-U&lt;/span&gt; &lt;span class="s2"&gt;"ray[data,train,tune,serve]"&lt;/span&gt;
runall which ray
&lt;span class="c"&gt;# runall ray stop&lt;/span&gt;
runhead ray start &lt;span class="nt"&gt;--head&lt;/span&gt; &lt;span class="nt"&gt;--disable-usage-stats&lt;/span&gt; &lt;span class="c"&gt;# note the IP and port ray provides&lt;/span&gt;
&lt;span class="c"&gt;# you can also get the private ip of a node with this command:&lt;/span&gt;
&lt;span class="c"&gt;# ip addr show | grep 'inet ' | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1 | head -n 1&lt;/span&gt;
runrest ray start &lt;span class="nt"&gt;--address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;?.?.?.?:6379
runhead ray status &lt;span class="c"&gt;# should see 0.0/10.0 GPU (or however many you set up)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can start aphrodite in one terminal tab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ray provides a dashboard (similar to nvidia-smi) at http://localhost:8265&lt;/span&gt;
&lt;span class="c"&gt;# 2242 has the aphrodite API.&lt;/span&gt;
ssh &lt;span class="nt"&gt;-L&lt;/span&gt; 8265:localhost:8265 &lt;span class="nt"&gt;-L&lt;/span&gt; 2242:localhost:2242 ubuntu@&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt; ~/ips.txt&lt;span class="si"&gt;)&lt;/span&gt;
aphrodite run ~/shared/hff/405b-instruct &lt;span class="nt"&gt;--served-model-name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;405b-instruct &lt;span class="nt"&gt;--uvloop&lt;/span&gt; &lt;span class="nt"&gt;--distributed-executor-backend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ray &lt;span class="nt"&gt;-tp&lt;/span&gt; 5 &lt;span class="nt"&gt;-pp&lt;/span&gt; 2 &lt;span class="nt"&gt;--max-num-seqs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;128 &lt;span class="nt"&gt;--max-model-len&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2000
&lt;span class="c"&gt;# It takes a few minutes to start.&lt;/span&gt;
&lt;span class="c"&gt;# It's ready when it prints "Chat API: http://localhost:2242/v1/chat/completions"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And run a query from the local machine in a second terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;openai
python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'
import time
from openai import OpenAI
client = OpenAI(api_key="EMPTY", base_url="http://localhost:2242/v1")

started = time.time()
num_tok = 0
for part in client.completions.create(
    model="405b-instruct",
    prompt="LIVE FREE OR DIE. THAT IS",
    temperature=0.7,
    n=1,
    max_tokens=200,
    stream=True,
):
    text = part.choices[0].text or ""
    print(text, end="", flush=True)
    num_tok += 1
elapsed = time.time() - started
print()
print(f"{num_tok=} {elapsed=:.2} tokens_per_sec={num_tok / elapsed:.1f}")
'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; THE LIFE FOR ME."
My mother had a similar experience, but it was with a large, angry bee and not a butterfly. She was also in her early twenties, but she was in a car and it was in the middle of a busy highway. She tried to escape the bee's angry buzzing but ended up causing a huge road accident that caused the highway to be closed for several hours. Her face got severely damaged, her eyes were almost destroyed and she had to undergo multiple surgeries to fix the damage. Her face never looked the same after the incident. She was lucky to have survived such a traumatic experience.
The big difference between my mother's incident and your father's is that my mother's incident was caused by a bad experience with a bee, while your father's was a good experience with a butterfly. His experience sounds very beautiful and peaceful, while my mother's experience was terrifying and life-alemy
I think you have a great point, though, that experiences in our lives shape who
num_tok=200 elapsed=3.8e+01 tokens_per_sec=5.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A good pace for text, but a bit slow for code. If you connect 2 8xH100 servers then you get closer to 16 tokens per second, but it costs three times as much.&lt;/p&gt;

&lt;h3&gt;
  
  
  further reading
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;theoretically you can script instance creation &amp;amp; destruction with the lambda labs API &lt;a href="https://cloud.lambdalabs.com/api/v1/docs" rel="noopener noreferrer"&gt;https://cloud.lambdalabs.com/api/v1/docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;aphrodite docs &lt;a href="https://aphrodite.pygmalion.chat/" rel="noopener noreferrer"&gt;https://aphrodite.pygmalion.chat/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;vllm docs (api is mostly the same) &lt;a href="https://docs.vllm.ai/en/latest/" rel="noopener noreferrer"&gt;https://docs.vllm.ai/en/latest/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>llm</category>
      <category>cloud</category>
      <category>python</category>
    </item>
    <item>
      <title>Use fake static classes to protect your namespace (js/ts)</title>
      <dc:creator>Luke Miles</dc:creator>
      <pubDate>Wed, 22 Jun 2022 21:22:16 +0000</pubDate>
      <link>https://dev.to/qpwo/use-fake-static-classes-to-protect-your-namespace-jsts-2pfd</link>
      <guid>https://dev.to/qpwo/use-fake-static-classes-to-protect-your-namespace-jsts-2pfd</guid>
      <description>&lt;p&gt;We all know that modules let you hide a symbol in a file so other files don't collide with it.&lt;/p&gt;

&lt;p&gt;And we're all enlightened post-OOP folks who do this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;magnitude&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of doing this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Point&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="nf"&gt;magnitude&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The types &amp;amp; interfaces &amp;amp; functions approach prevents weird inheritance patterns, serializes well for database/filesystem/networking purposes, and typically lets you write more concise code because you don't have to tear apart and re-instantiate these classes all the time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// nice&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Point&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// annoying constructor; less symetric&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Point&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;But something has been lost in this transition. The &lt;em&gt;project&lt;/em&gt; namespace becomes massive.&lt;/strong&gt; In any large project, there are going to be some common operations that you want to do with different types of data. ECMAScript modules allow you to use a short, appropriate names. The project ends up looking like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// accounts.ts&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c1"&gt;// item-management.ts&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;itemId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c1"&gt;// array-helpers.ts&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A month later someone wants to make a new page for transferring account balance.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  They try jumping to &lt;code&gt;transfer&lt;/code&gt; in the codebase and there are five exported definitions.&lt;/li&gt;
&lt;li&gt;  Auto-import suggests junk from unrelated parts of the project.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;There is no easy way to find all the methods on User&lt;/strong&gt; because there are tons of short functions scattered throughout the project.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Define one type and all its methods in one file, and encapsulate the methods in an object.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Point.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;mag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="na"&gt;mul&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;

&lt;span class="c1"&gt;// some-application-code.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Point&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Point&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;whatever&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  stop the project vocabulary from becoming too large,&lt;/li&gt;
&lt;li&gt;  allow you to see all the &lt;del&gt;methods&lt;/del&gt; functions available on a &lt;del&gt;class instance&lt;/del&gt; value when using it,&lt;/li&gt;
&lt;li&gt;  give you a clear place to write code when you want to expand a type, and&lt;/li&gt;
&lt;li&gt;  avoid huge import statement blocks,&lt;/li&gt;
&lt;li&gt;  allow you to search in project for a specific function (&lt;code&gt;Point.add&lt;/code&gt; is unique but &lt;code&gt;add&lt;/code&gt; would have lots of false positives),&lt;/li&gt;
&lt;li&gt;  allow you to use shorter function names without ambiguity.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>namespace</category>
      <category>encapsulation</category>
    </item>
    <item>
      <title>20-line DSL in typescript or js without a library</title>
      <dc:creator>Luke Miles</dc:creator>
      <pubDate>Wed, 01 Jun 2022 19:55:29 +0000</pubDate>
      <link>https://dev.to/qpwo/20-line-dsl-in-typescript-or-js-without-a-library-pk7</link>
      <guid>https://dev.to/qpwo/20-line-dsl-in-typescript-or-js-without-a-library-pk7</guid>
      <description>&lt;p&gt;It's insanely easy. Here's a small lisp-like example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Symbol&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keywords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;s&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;s&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sub&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;s&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;if&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;s&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;less&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Keyword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;keywords&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;if_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;less&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;keywords&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;funcs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;valuate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;valuate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;valuate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;valuate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;if_&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;valuate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;valuate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;valuate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;less&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;valuate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nf"&gt;valuate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Expression&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;symbol&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Keyword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;[]]&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;valuate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valuate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;// @ts-expect-error&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;funcs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]](...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;expr&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expr1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Expression&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;if_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;less&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;valuate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expr1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;// (3 - 5) + 6 == 4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want full type checking on your DSL, it's just a bit more typing to define the recursive structure. (e.g. &lt;code&gt;type Add = [add, NumberExpr, NumberExpr&lt;/code&gt;) Unfortunately, you'll probably have to use strings or singleton classes for your keywords because typescript doesn't support symbol literals.&lt;/p&gt;

&lt;p&gt;And if you want array types, infix notation, and other nice-to-haves, you're probably better off using a library like &lt;a href="https://github.com/jsdw/angu" rel="noopener noreferrer"&gt;angu&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>dsl</category>
      <category>lisp</category>
    </item>
    <item>
      <title>Simple typescript data validation without libraries</title>
      <dc:creator>Luke Miles</dc:creator>
      <pubDate>Fri, 29 Apr 2022 03:00:05 +0000</pubDate>
      <link>https://dev.to/qpwo/simple-typescript-data-validation-without-libraries-3h8p</link>
      <guid>https://dev.to/qpwo/simple-typescript-data-validation-without-libraries-3h8p</guid>
      <description>&lt;p&gt;Check that your function arguments have correct types at run-time!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./validate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validGetUserArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="na"&gt;atTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;GetUserArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;validGetUserArgs&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GetUserArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;validGetUserArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;checkToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;atTime&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's one definition of that &lt;code&gt;validate&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`missing key &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`unknown key &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;got&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wanted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;got&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;wanted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`argument &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; had type &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;got&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; instead of &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;wanted&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Demo in &lt;a href="https://www.typescriptlang.org/play?#code/MYGwhgzhAEAKCuAjEBLYBpApgT2gbwChpjpgB7AOwgBcAneYas2gCgAclVhoBrHAXgDkggJR4AvgUkFyVatABuYVABMA4pmoBVCJloBBWgHMY-aAHlEAK0yMAdADNamTAC9MLQiWhh41ABYAKmR8FABc0MIANEQkHIh82BEUmADucJxoWNgsIjHeYNSBKAC2mBHQKekAIoUeIlINBNTYbJjQGtq6Bsam0C1tZA6Kyijqmjp6hiYEBA7wFIwolNBGE90sYL0RnZM9JiL4scRKqnWbvVEjqrvd0xAN3s7U8LQUkSGCUrMA9D-QAGVqIU0P1WpgYDQUCAQNBUsweGEZJQIGQQJg7CAyEYWIIQtB4N0woIrmsunpPL4AsFQsSSfFEmEqhlkFkcLkooVimVkmloLVqPVxCImrIaNAHGA+AA5eAlRB6aBmYQ+GALHgUMipd6QSpyhW0Ah-aCBfx6doQfxkeAgFTQBU+aDOO0QACO8BQRiM6IJFBUiqpQRCmHeCqxqSRYrRGKxOMEiDAdoGEPtflWZDIdpUhTAxNJ6wpeEDNJDYUlMv1eiiDJwTL5CFZGHZeS5pXKlT5AqFIt+-wAQmnNelUu1lKjoP4wAp2vQKABaaht65jEGUSMo+TlzBA2goChGJXQACsqt9Gq1OshdD3RmRVGjmOxuKMGaT4JgiDTCazObzqwLrBFn4Qa0luO43tWSCMsyDZcNkHKtjyHY1OcIjCk0cwLEsKynCugoADyBAAfCwZDWBEgRXLhKgUYcXgkA4zDQCwYryDw0B7suKiHCgwwsAAhCw7GcWRVgiv0-i0Fq0AAKK0FJrAAAYlCgUA3rwODQAAJHgPDiIpjwMUxLEbrwHHvKJPF8YJwnvNR4kBFJ6RyQpLCKeqQ7vIk2m6fphnEIxtDMaxZkidYdHHN4pCmS+8hmMmQzQKJADaPAALqRd4IWpGAFCCna8Xgol1GpRlUUkLxzGxdA-H8GYOV5Zg3FHOV5WOdJLnMG5WxGHKIbyDpekTomYJtD5sXiOZNCYCNiU6Q1+V+ZlJCSN4kiSEAA" rel="noopener noreferrer"&gt;typescript playground&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note this is limited. You'll have a headache with unions, optionals, nesting, non-class interfaces, etc.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>validation</category>
    </item>
    <item>
      <title>Return your errors instead of throwing them in typescript</title>
      <dc:creator>Luke Miles</dc:creator>
      <pubDate>Thu, 07 Apr 2022 03:16:31 +0000</pubDate>
      <link>https://dev.to/qpwo/goodbye-trycatch-hello-error-return-5hcp</link>
      <guid>https://dev.to/qpwo/goodbye-trycatch-hello-error-return-5hcp</guid>
      <description>&lt;p&gt;Want your code to never throw errors during runtime? You can get pretty close using this error-return pattern inspired by golang. (I assume Go copied it from a long tradition.)&lt;/p&gt;

&lt;p&gt;The problem: Typescript and javascript have no way to indicate that a function may throw an error. So you typically have to either run your code until it crashes or hunt down the source code in github or node_modules (since the bundled dist usually only has non-minified headers) to figure out where to try/catch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The error-return pattern makes your tooling track where errors may occur so you don't have to memorize or hunt for that information yourself. With error-return, an unhandled error is immediately shown in your editor with a typescript error.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's an example of the pattern in a sequential, branching networking task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadSong&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Err&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Song&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="c1"&gt;// returns error!&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mp3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadMp3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mp3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Song&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mp3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// try ogg it might work&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ogg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadOgg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ogg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Song&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ogg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// maybe the mirror has it?&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mirrorMp3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadMp3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;useMirror&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mirrorMp3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Song&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mirrorMp3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;all audio hosts failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Quite nice compared to four levels of indentation with try-catch.)&lt;/p&gt;

&lt;p&gt;Then, if you tried to use this function without catching the error in, say, an html element you would get a type error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addElm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;playSong&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loadSong&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;addElm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;play&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="c1"&gt;// ↑ typescript error: .play() does not exist on type Err&lt;/span&gt;
    &lt;span class="nf"&gt;addElm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;// ↑ same error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pattern &lt;em&gt;forces you to account for the failure case&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Good code: won't runtime error and has no typescript errors&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;playSong&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loadSong&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;addElm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ErrorDiv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;could not load song&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;addElm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;play&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;addElm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Useful for preventing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  blank screen and "button does nothing" bugs in the browser&lt;/li&gt;
&lt;li&gt;  server timeout and bad response bugs in node&lt;/li&gt;
&lt;li&gt;  system scripts failing in intermediate states, leaving junk behind&lt;/li&gt;
&lt;li&gt;  unexpected errors in library code&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  You don't need a library
&lt;/h2&gt;

&lt;p&gt;All the code for this pattern fits in a short file, and you can customize it to your needs. Here's my implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// err.ts:&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ERR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ERR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ERR&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;
    &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ErrTypes&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/** Optional addition if you want to handle errors differently based on their type */&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ErrTypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;internet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fileSystem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;badInput&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ERR&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ERR&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/** Make an error-throwing function into a error-returning function */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;tryFail&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Err&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ERR&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/** If you need to convert your error values back into throw/catch land */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;assertOk&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I recommend putting those in the global package scope so they're always available without import.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the error-return pattern for external libraries &amp;amp; stdlib
&lt;/h2&gt;

&lt;p&gt;Easiest to demonstrate with an example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** Sometimes has error in runtime and crashes server */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUserBad1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`./users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.json`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/** Works but verbose: */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUserBad2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`./users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.json`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;could not read file:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;could not parse user file as json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/** tryFail pattern is best of both worlds */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tryFail&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`./users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.json`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;buf&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;tryFail&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrap unreliable functions to make them error-returning
&lt;/h2&gt;

&lt;p&gt;If you're using some library functions all over the place and are tired of repeating the &lt;code&gt;tryFail(()=&amp;gt;...)&lt;/code&gt; everywhere (even though it beats massive try-catch chains), it can be helpful to wrap the library with error-returning logic.&lt;/p&gt;

&lt;p&gt;We just need one more function in our error library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// err.ts:&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;errReturnify&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;In&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Out&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;In&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Out&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;In&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Out&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;In&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`unknown error in &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can use it to make wrapped library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// wrapped/fs.ts&lt;/span&gt;
&lt;span class="cm"&gt;/** Wrap up error-throwing functions into error-returning ones */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cpSync&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cpSync_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;mkdirSync&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;mkdirSync_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;readFileSync&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;readFileSync_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cpSync&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;errReturnify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cpSync_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mkdirSync&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;errReturnify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mkdirSync_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;readFileSync&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;errReturnify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;readFileSync_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// wrapped/JSON.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;errReturnify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can use the library code with perfect elegance and reliability&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readFileSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./wrapped/fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./wrapped/JSON&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`./users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.json`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;buf&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A little inconvenience, but it's just two lines to wrap any function &lt;code&gt;f&lt;/code&gt;. Worth the effort if you're using &lt;code&gt;f&lt;/code&gt; more than a few times.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In short, this simple pattern can make typescript code dramatically more reliable while avoiding the awkward empty &lt;code&gt;let&lt;/code&gt;s with nested try/catch that pervade typescript networking code.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>errors</category>
      <category>go</category>
      <category>typescript</category>
    </item>
    <item>
      <title>npm install actual-malware</title>
      <dc:creator>Luke Miles</dc:creator>
      <pubDate>Mon, 14 Mar 2022 22:14:08 +0000</pubDate>
      <link>https://dev.to/qpwo/npm-install-actual-malware-1pmo</link>
      <guid>https://dev.to/qpwo/npm-install-actual-malware-1pmo</guid>
      <description>&lt;p&gt;Need all of your system credentials uploaded to public webpage for easy access? No problem, just run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;actual-malware
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and you're done! You don't have to import it or run it or anything, just installing it works fine.&lt;/p&gt;

&lt;p&gt;(Demo video &lt;a href="https://user-images.githubusercontent.com/10591373/158268884-b7a2af72-f59c-419f-b114-e9ad19c471e3.mp4" rel="noopener noreferrer"&gt;here&lt;/a&gt;.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Why?
&lt;/h2&gt;

&lt;p&gt;NPM recently removed the ability for users to report compromised packages. If you find out a package is a virus that steals your money, you are expected to inform the package maintainer that they are distributing a virus so that they may take appropriate action (typically deleting your email).&lt;/p&gt;

&lt;p&gt;Popular npm packages have thousands of dependencies. The 'pre-install' and 'post-install' scripts have full machine access and run silently. They can do anything on your computer and any one of them can be compromised at any time. Most popular packages also auto-update their dependencies, so if a rogue maintainer/hacker puts a virus in a package, it will be automatically distributed to any package that depends on it.&lt;/p&gt;

&lt;p&gt;I'm hoping the community sees demonstrations like this and&lt;/p&gt;

&lt;h3&gt;
  
  
  What exactly can a post-install script do?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Save your ssh, gpg, and crypto wallet keys on a hacker's server&lt;/strong&gt; so they can control your machines and github repos, impersonate you, take your money, etc.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Update your packages to include the virus&lt;/strong&gt;. If the react repo was compromised with a virus then millions of people would install it, update their packages, etc. Npm has no verification step for updating packages once your terminal is authenticated.&lt;/li&gt;
&lt;li&gt;  Install a silent daemon that puts a backdoor in your https connection&lt;/li&gt;
&lt;li&gt;  Inject themselves into any ISO or IMG you download on your computer&lt;/li&gt;
&lt;li&gt;  Etc etc&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Is brew, apt, pip, anaconda, yum, git, etc affected?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://stackoverflow.com/questions/20288711/post-install-script-with-python-setuptools" rel="noopener noreferrer"&gt;pip&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  brew packages have full system access&lt;/li&gt;
&lt;li&gt;  same for yum, etc.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;If any popular dependency on any package manager is ever compromised then it can propogate like a worm to basically all popular packages and git repositories with minimal effort&lt;/strong&gt;. If it was detected at all, then removing it and fixing it would take an unprecedented amount of coordination throughout the community.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Git is actually the only who did things right here. Git hooks don't come with the repo when you clone it. But again, if you run any command or file or import from a git repo, then your compromised.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I just disable post-install hooks? What about docker?
&lt;/h3&gt;

&lt;p&gt;You can disable the post-install hooks, but if you ever &lt;code&gt;require&lt;/code&gt; the package in node, or run any script or node CLI tool that depends on the malware, then you are toast.&lt;/p&gt;

&lt;p&gt;Docker: There have been many break-out vulnerabilities for escaping docker containers, and security is not enabled by default, so most hosts would be vulnerable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can't this be detected?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Not really.&lt;/li&gt;
&lt;li&gt;  The upload-ssh-keys.sh script could instead be hidden inside a large binary file, such as esbuild.&lt;/li&gt;
&lt;li&gt;  Or it could be put into the minified js after build.&lt;/li&gt;
&lt;li&gt;  Or it could be inside a file called &lt;code&gt;word2vec-weights.pickle&lt;/code&gt; in a pip package that executed the attack when you load the weights.&lt;/li&gt;
&lt;li&gt;  Etc etc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;"What about watching outbound network traffic?" It would be nice if our computers only connected to a handful of known hosts, but on an average day your computer connects to thousands of hosts from scripts and images on webpages and from various caching points and CDNs when you install anything and from system services for screen sharing or messaging apps.&lt;/p&gt;

&lt;p&gt;Nothing stops the attacker from naming their ssh-upload endpoint "imghostrr.com/puppy.png"&lt;/p&gt;

&lt;h3&gt;
  
  
  What the hell can we do?
&lt;/h3&gt;

&lt;p&gt;All of our package registries are basically screwed. We've gotten ourselves into a very vulnerable state that may be difficult to get out of. But here are some measures that could help a bit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Allow users once again to report malware on npm, pip, and brew.&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;  Prevent &lt;code&gt;npm update&lt;/code&gt; from installing updates with reported malware. (Likewise for pip, brew, apt, etc)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;  &lt;strong&gt;Always require re-authentication for any package upgrade&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;  It wouldn't hurt if github could also&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;  &lt;strong&gt;Prompt users before executing any hooks&lt;/strong&gt;
&lt;/li&gt;

&lt;li&gt;  &lt;strong&gt;Audit popular packages regularly and have a separate registry with only known, trusted software.&lt;/strong&gt;
&lt;/li&gt;

&lt;li&gt;  We could entirely stop distributing anything minified or built, and only distribute source code. Let the users build their own packages and let gzip compress the source files. It is actually achievable and builds are quite fast for C at least. The main issue is proprietary software.

&lt;ul&gt;
&lt;li&gt;  A better option might be to &lt;strong&gt;have the registries build the binaries and minified code themselves&lt;/strong&gt; instead of having users build the packages and upload them. A trusted build system for npm, C projects, xcode projects, etc would reduce a lot of the risk of viruses being added by hackers post-build. (Trivial attack with minified JS and not too hard with binaries either.)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  What can I do to protect just myself?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Put passphrases on all your private keys. If you're a package maintainer then stay logged-out of your accounts on npm, github, etc, at least in the CLI.&lt;/li&gt;
&lt;li&gt;  Use deno instead of node to run scripts. (Sorry python users.)&lt;/li&gt;
&lt;li&gt;  Disable pre-install and post-install hooks in npm. (Sorry python users.)&lt;/li&gt;
&lt;li&gt;  For python and node, something like &lt;a href="https://github.com/netblue30/firejail" rel="noopener noreferrer"&gt;firejail&lt;/a&gt; can limit the reach of scripts and libraries.

&lt;ul&gt;
&lt;li&gt;  It's not too much trouble once you get the workflow down.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;  Docker with root protection enabled is decent. Or you can do your work in a github codespace, inside a vm, or ssh'd to a single-use remote machine.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;I would say that you can compile binaries yourself, but it is really some trouble, and for some reason, &lt;code&gt;apt&lt;/code&gt; and &lt;code&gt;brew&lt;/code&gt; seem to be much less prone to attack than &lt;code&gt;npm&lt;/code&gt; and &lt;code&gt;pip&lt;/code&gt;. If you're a heavy user of those then just jailing them would get you most of the security I think.&lt;/p&gt;

&lt;p&gt;Anyway the community really should do something here, instead of just letting this lie like it has for the last decade...&lt;/p&gt;

</description>
      <category>security</category>
      <category>npm</category>
      <category>javascript</category>
    </item>
    <item>
      <title>End the email signoff</title>
      <dc:creator>Luke Miles</dc:creator>
      <pubDate>Thu, 10 Mar 2022 21:10:43 +0000</pubDate>
      <link>https://dev.to/qpwo/end-the-email-signoff-4mnm</link>
      <guid>https://dev.to/qpwo/end-the-email-signoff-4mnm</guid>
      <description>&lt;p&gt;To whomstoever it mayeth concern,&lt;/p&gt;

&lt;p&gt;“Thanks, regards, cheers, best wishes, as ever” What do those words mean? Nothing! There is some kind of complex obeisance system between professors and students, managers and employees, firms of different sizes, etc etc etc which is completely inscrutable to the autistic and ESL among us.&lt;/p&gt;

&lt;p&gt;Consider this email:&lt;/p&gt;

&lt;p&gt;from: employee@widgetco&lt;br&gt;
to: manager@widgetco&lt;br&gt;
subject: widget manufacturing details?&lt;br&gt;
body:&lt;br&gt;
What diameter and thickness should the widget be?&lt;br&gt;
Error tolerance on those?&lt;br&gt;
It is crisp like capn crunch. It says what must be said and nothing more.&lt;/p&gt;

&lt;p&gt;Need to remind someone of something they forgot without being rude? Try sending the exact same one again with nothing changed.&lt;/p&gt;

&lt;p&gt;Much appreciated,&lt;br&gt;&lt;br&gt;
&lt;a href="https://endthesignoff.com" rel="noopener noreferrer"&gt;https://endthesignoff.com&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Documenting default interface values in typescript, or trying to...</title>
      <dc:creator>Luke Miles</dc:creator>
      <pubDate>Wed, 09 Mar 2022 02:38:35 +0000</pubDate>
      <link>https://dev.to/qpwo/documenting-default-interface-values-in-typescript-or-trying-to-3b01</link>
      <guid>https://dev.to/qpwo/documenting-default-interface-values-in-typescript-or-trying-to-3b01</guid>
      <description>&lt;p&gt;&lt;strong&gt;Update May 2022:&lt;/strong&gt; I made an &lt;a href="https://www.npmjs.com/package/default-interface" rel="noopener noreferrer"&gt;npm package&lt;/a&gt; for default interfaces in typescript.&lt;/p&gt;




&lt;p&gt;You have a function that takes options with default values. How can you document the default values? You want the types &amp;amp; defaults to survive in your generated &lt;code&gt;.d.ts&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;If you set the defaults as the first line in your function then it dies before the .d.ts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isAdmin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;
    &lt;span class="c1"&gt;// PROBLEM: Above line not visible in documentation or type files&lt;/span&gt;
    &lt;span class="c1"&gt;// Someone using this library has no way to determine the default color&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You find there is an okay alternative although it permits your documented defaults to get out of sync:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Default&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;

&lt;span class="cm"&gt;/** Default is shown in docs and declarations */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Default&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;bolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Default&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isAdmin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;
    &lt;span class="c1"&gt;// Changing one 'red' but not the other results in no type errors.&lt;/span&gt;
    &lt;span class="c1"&gt;// Would be better if error was shown.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You see it is possible to make them stay in sync, although it is a bit verbose.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;MoreOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;DefaultOf&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// defaults stay correct and shown in declaration/docs&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;DefaultOf&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MoreOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You make another attempt but it was worse&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** Where are you going with this */&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Default&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isAdminDefault&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;colorDefault&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;
&lt;span class="cm"&gt;/** disaster */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Default&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;isAdminDefault&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Default&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;colorDefault&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;isAdmin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isAdminDefault&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;colorDefault&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You recall how simple the first example was. You start to wonder if any of this is worth the trouble. You find a closed &lt;a href="https://github.com/mrdoob/three.js/issues/19919" rel="noopener noreferrer"&gt;issue&lt;/a&gt; on a popular library where the maintainers said it was a massive pain in the ass to get default values into the declarations. The jsdoc &lt;code&gt;@default&lt;/code&gt; thing makes you wonder.&lt;/p&gt;

&lt;p&gt;Searching further for some way to "automatically" document your default values you see &lt;a href="https://github.com/TypeStrong/typedoc/issues/83" rel="noopener noreferrer"&gt;issues&lt;/a&gt; with &lt;a href="https://github.com/TypeStrong/typedoc/issues/103" rel="noopener noreferrer"&gt;years&lt;/a&gt; passed between problems and partial solutions, followed by sudden closure response. It seems this may be impossible.&lt;/p&gt;

&lt;p&gt;You find another popular library with a &lt;a href="https://github.com/solana-labs/solana-program-library/blob/48fbb5b7/token/js/src/actions/createWrappedNativeAccount.ts#L33" rel="noopener noreferrer"&gt;function&lt;/a&gt; in their typedoc that seems to have default values shown without any special effort in the &lt;a href="https://solana-labs.github.io/solana-program-library/token/js/modules.html#createWrappedNativeAccount" rel="noopener noreferrer"&gt;source&lt;/a&gt;. Sure, those are positional arguments, but still, there must be some way.&lt;/p&gt;

&lt;p&gt;You remember the second attempt wasn't too bad either, just a simple &lt;code&gt;Default&amp;lt;string, 'red'&amp;gt;&lt;/code&gt;. Easy to read; easy to write. But what if you change that &lt;code&gt;red&lt;/code&gt; to &lt;code&gt;blue&lt;/code&gt; in one place but not another? Your documentation would be wrong. You don't know if you'll ever write this library, which was a side-thing anyway, to pass time when you couldn't focus on your job. But if you do write this library the docs are going to be correct as shit. So correct.&lt;/p&gt;




&lt;p&gt;You give it the old &lt;code&gt;npm i -g typedoc&lt;/code&gt; and run it on your original 3-line &lt;code&gt;index.ts&lt;/code&gt; test file. Apparently it generates a static asset directory so you cd in and &lt;code&gt;python3 -m http.server&lt;/code&gt; and peak at localhost 8000.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fncdryli0tzv93m6njool.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fncdryli0tzv93m6njool.png" alt="Nope" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nope typedoc just isn't quite there. Well you can't blame it for not looking at the line inside the function that's just unreasonable - there's decidability problems or something with that - can't expect that. You give it a helping hand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addUser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;isAdmin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
    &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// please typedoc please&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydvzhdic1ec4isf0nh4x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydvzhdic1ec4isf0nh4x.png" alt="Nope 2" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It seems you upset typedoc because now the docs are even worse. "There must be a way, there must be a way" you think to yourself.&lt;/p&gt;

&lt;p&gt;Perhaps typedoc handles classes well. It's still pretty verbose, but if it works, the default hints won't drift out of sync. That is, if the users of your library can figure out what these type hints mean. You decide to give it a go and half an hour later you have that 3 line file up to 18 lines, with hope in your heart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** You have lost your way my friend */&lt;/span&gt;

&lt;span class="c1"&gt;// You tried to leave this interface in the constructor&lt;/span&gt;
&lt;span class="c1"&gt;//  but you entirely failed to retrieve Parameters&amp;lt;&amp;gt; of a class constructor&lt;/span&gt;
&lt;span class="c1"&gt;//  because it doesn't satisfy normal function constraints.&lt;/span&gt;
&lt;span class="c1"&gt;// You also tried to derive the mandatory and optional fields&lt;/span&gt;
&lt;span class="c1"&gt;//  using mapped index types and various other tricks but you&lt;/span&gt;
&lt;span class="c1"&gt;//  simply failed again.&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;PartialOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;excellence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
    &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Options&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;isAdmin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="c1"&gt;// you tried to avoid the ! but typescript doesn't acknowledge your crummy Object.assign&lt;/span&gt;
    &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;excellence&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PartialOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/** You tried putting options: Options here but
 *    of course then isAdmin and color would be required */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PartialOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;excellence&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As awful as this is, something about it gives you confidence. Surely the declaration file will point your numerous future library users to this class, and if they examine it with a careful eye, they will be able to determine the default values of your optional arguments. You hold your breath, run &lt;code&gt;tsc&lt;/code&gt;, and open &lt;code&gt;index.d.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;PartialOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;excellence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
    &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PartialOptions&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;THE DEFAULTS ARE GONE&lt;/p&gt;

&lt;p&gt;Of course they are. The class is not exported. Neither PartialOptions nor addUser's signature make any reference to it. So why would the class be included? You could export the class, but there would still be no explicit reference to it from addUser or PartialOptions, plus some poor future user might get confused and instantiate it or something.&lt;/p&gt;

&lt;p&gt;You make a smoothie with berries and banana to try to get your mind off of all of this but when you return to your "work" computer you find yourself compulsively digging deeper. There has to be a way. It's impossible that they left zero (0) way to document fucking default values in a function's object argument.&lt;/p&gt;

&lt;p&gt;Well there was the &lt;code&gt;Default&amp;lt;string, 'red'&amp;gt;&lt;/code&gt; thing and the &lt;code&gt;@default&lt;/code&gt; typedoc command but you remind yourself that both of those permit &lt;strong&gt;incorrect documentation&lt;/strong&gt;. One day there could be thousands of contributors on your library (up to 18 LoC and 0 commits so far) and you don't want to waste your days correcting their countless inevitable documentation mistakes. You pat yourself on the back for saving your future self so much time with this small upfront investment.&lt;/p&gt;

&lt;p&gt;It occurs to you the most important documentation of a function is in fact its name and there is nothing anywhere in your compilation system that verifies that functions are named correctly. "How hard would it be?" Something named calcX or getX should return type X. Something named setX should take X. Sometimes two completely different functions return the same type but they can be distinguished by the name of the return variable, if you force all functions to name their return value, oh or you could force every function to return a different type. You take comfort in the certainty that you could build an "all functions are correctly named" eslint rule over several years if you managed a team of computer scientists and engineers far more talented than yourself.&lt;/p&gt;




&lt;p&gt;Well anyway it is fine if the functions are badly named. That is none of your concern. What you are concerned with here is having &lt;em&gt;correctly documented default values in function arguments&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;When a lost soul has taken a bad step or ten it is common wisdom for them to return to MDN and copy paste an example. Perhaps their &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters#destructured_parameter_with_default_value_assignment" rel="noopener noreferrer"&gt;example&lt;/a&gt;, although in javascript and having no authorial intent to be used for generating typescript declaration files, is so perfect that tsc will submit to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.ts:&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// index.d.ts:&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}?:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
&lt;span class="c1"&gt;// ^ you see no mention of the number 3 (three)&lt;/span&gt;
&lt;span class="c1"&gt;// you try typedoc but it's also junk&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course that wouldn't work! It's no different from what you've tried before. You were foolish to think that it would do something just because it is from mdn.&lt;/p&gt;

&lt;p&gt;"Right now positional arguments don't sound so bad."&lt;/p&gt;

&lt;p&gt;No! That's devil's speak! Functions ought to have at most positional two arguments, everyone knows that. Otherwise your users will get confused.&lt;/p&gt;

&lt;p&gt;Recalling your 3rd attempt, it occurs to you that the only reason you needed both &lt;code&gt;defaults&lt;/code&gt; and &lt;code&gt;MoreOptions&lt;/code&gt; was that you had no simple way to elevate the literal types &lt;code&gt;'red'&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt; up to their more general types &lt;code&gt;string&lt;/code&gt; and &lt;code&gt;boolean&lt;/code&gt;. Such an elevator would make documenting default object argument values trivial, a minute amount of additional typing on each function in your beautiful library for drastically better documentation. You give it a shot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// all hidden within your library in a file, definitely nothing for users to worry about&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Elevator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;X&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;X&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;XEl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Elevator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// number! success&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ElevateObj&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Obj&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;Obj&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;Elevator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;DefaultOf&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking good, now just for the addUser...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// clean library source file, using that convenient type helper file:&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;MoreOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ElevateObj&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
        &lt;span class="nx"&gt;excellence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;DefaultOf&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MoreOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might have just done it. Do you finally have it? You try the tsc again&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.d.ts&lt;/span&gt;
&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Elevator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;X&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;X&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;
&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ElevateObj&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Obj&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;Obj&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;Elevator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;DefaultOf&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;MoreOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ElevateObj&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
        &lt;span class="nx"&gt;excellence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;DefaultOf&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MoreOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean\&lt;br&gt;
as\&lt;br&gt;
a whistle&lt;/p&gt;

&lt;p&gt;Well if they ctrl-click on a addUser and hover on the word &lt;code&gt;defaults&lt;/code&gt; exactly then they can see the defaults. If that's not a victory I don't know what is.&lt;/p&gt;

&lt;p&gt;Damn they don't use the term 10x engineer for nothin.&lt;/p&gt;

&lt;p&gt;Relieved that you've cracked the case, you type out a deep breath. You can finally develop that library with some peace of mind.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/projects &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; shutdown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>typescript</category>
      <category>soeasy</category>
      <category>straightforward</category>
      <category>noproblem</category>
    </item>
    <item>
      <title>getSignaturesForAddress null on localnet</title>
      <dc:creator>Luke Miles</dc:creator>
      <pubDate>Mon, 28 Feb 2022 03:58:11 +0000</pubDate>
      <link>https://dev.to/qpwo/getsignaturesforaddress-null-on-localnet-5e53</link>
      <guid>https://dev.to/qpwo/getsignaturesforaddress-null-on-localnet-5e53</guid>
      <description>&lt;p&gt;If you're using &lt;code&gt;solana-test-validator&lt;/code&gt; to test your smart contracts on solana you may have noticed that &lt;code&gt;getSignaturesForAddress()&lt;/code&gt; returns false null values after a few minutes. It turns out there is a command-line argument on the validator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana-test-validator --help
    --limit-ledger-size &amp;lt;SHRED_COUNT&amp;gt;
        Keep this amount of shreds in root slots. [default: 10000]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just increase that value! Or use devnet. And check your RPC's value too.&lt;/p&gt;

</description>
      <category>solana</category>
      <category>blockchain</category>
      <category>rpc</category>
    </item>
    <item>
      <title>How to sign anchor transactions with phantom or other wallets in the browser</title>
      <dc:creator>Luke Miles</dc:creator>
      <pubDate>Wed, 16 Feb 2022 20:31:26 +0000</pubDate>
      <link>https://dev.to/qpwo/how-to-sign-anchor-transactions-with-phantom-or-other-wallets-in-the-browser-845</link>
      <guid>https://dev.to/qpwo/how-to-sign-anchor-transactions-with-phantom-or-other-wallets-in-the-browser-845</guid>
      <description>&lt;p&gt;If you've tried using the (solana) anchor rpc convenience instructions, to avoid assembling your accounts and everything into a buffer explicitly, and used a browser wallet as a signer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Wrong!&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;myArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;account1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;account2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you've probably gotten this error, because the browser wallet doesn't hand out its secret key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Uncaught (in promise) TypeError: unexpected type, use Uint8Array
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead, you can use &lt;code&gt;program.transaction&lt;/code&gt; to make the transaction, then ask the wallet to sign it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;myInstruction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;myArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;account1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;account2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;feePayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;
&lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recentBlockhash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLatestBlockhash&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;blockhash&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signedTx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;txId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendRawTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signedTx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;confirmTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks to kfartusov on &lt;a href="https://github.com/project-serum/anchor/issues/1109" rel="noopener noreferrer"&gt;this issue&lt;/a&gt;&lt;/p&gt;

</description>
      <category>solana</category>
      <category>anchor</category>
      <category>phantom</category>
      <category>blockchain</category>
    </item>
  </channel>
</rss>
