Plan
- phase 1:
- BUILD TOOL FOR REDWOOD CORE
- monoproject structure
- x9.build
- limited to custom builds for what redwood needs
- npm packages, clis, vscode extension, language server,
- x9.playground, x9.test
- x9.convert? x9.is? (controversial)
- simple macros: x9.preval, x9.preval.expr
- so we can have strongly typed templates!
- phase 2:
- BUILD TOOL FOR MORE PROJECTS (that want to escape monorepo hell)
- x9.build++
- more complex programmatic builds including automatic dev environments and e2e testing
- x9.bridge:
- to “export” code “into” any project with its own build process (ex: redwood, next)
- x9.cloud:
- speed up builds by sharing a cache with a team (stealing idea from bazel, nx)
- offload some compilation/editing work to the cloud. this will come in handy if people turn on type-driven features and fully embrace the monoproject structure (this might be appealing to large companies with humongous projects. using the full power of the type system could easily increase their productivity 2x, but it overloads the compiler)
- phase 3:
- BUILD TOOL FOR MORE + SIMPLE CLOUD
- x9.cloud.lambda, allowing transparent deployment of lambdas for any part of your code (they get wired+deployed automatically and “just work”)
- x9.bridge and x9.external.project:
- to “export” code from your lambdragon codebase “into” any project with its own build process (ex: redwood, next). You keep most code in lambdragon, and export libraries. But we make this process easy for each
- phase 4:
- CLOUD!
- x9.cloud.lambda += docker expressions
- x9.cloud.timer, x9.cloud.queue
- add support for more AWS CDK constructs. Like pulumi tried to do, but building on a better foundation
Strategy
There is a general “technical” strategy drives the roadmap: We introduce some elements early-on that won’t seem like radical departures, but they are key to enabling completely novel features later. The end-game is extremely ambitious, but we can’t just jump there.
The phased approach gives us time to work on DX, mature the technology, and make sure users get it before throwing something completely new at them.
It also allows us to enter the market, raise $, and avoid becoming a vaporware startup. Which is a common destination for software startups that requires hard-code R&D to achieve their dreams.
Here are some examples of early elements that are a stepping stone towards bigger and bolder features:
- Programmatic builds
- Phase 1: Programmatic builds will be used for “common” things like NPM packages, vscode extensions, etc. Compared to the status quo they are a DX improvement, for sure, but they don’t enable anything radically “new”. In other words, developers can achieve the same today with other tools, but…
- Phase 2: The code-driven approach (building a graph of objects/resources/constructs that describe the end-state of your system) can scale beyond these traditional build targets and into infrastructure. This is already being done by Pulumi and the CDK. What has not being done before is blurring the line between traditional builds and cloud deployments. By the time we introduce cloud elements (ex: lambdas, databases, queues, etc) developers will already know how to use our build system and we will have a great DX. Another thing we can achieve by doing this is going after a completel new market (the CDK/Pulumi go after devops first, with devs being only a recent initiative). We are a build tool that will seem to magically grow dragon wings.
- Dynamic Bundles (aka closure serialization):
- Phase 1: this feature will be used under the hood by the build targets that consist of several components that need to talk to each other (ex: the vscode extension + webviews + language server, or the CLI + local node.js workers). The
x9.capture
API will be used under the hood. This will give us time to improve closure serialization and its interaction with bundling
- Phase2: x9.capture will be the basis for the full cloud programing model (ex: creating a database and adding an “onRecordAdded” callback as if you were dealing with local objects)
- Playgrounds
- Phase 1: Playgrounds provide an incremental improvement over similar tools (ex: Quokka, Wallaby, Swift playgrounds). They will be central to the user workflow. We will slowly add more interactive features
- Phase 2: Code driven commands
- Some parts of the
x9
API, when used within a playground, will trigger “actions” like starting builds, refactoring code, etc. (ex: x9.dev.refactor(function1).to("...autocomplete")
, x9.runTask(somefunction)
). You can even get data and view reports (x9.analyze(MyComponent)
).
- Phase 3: Cloud Management
- View runtime logs (ex:
x9.dev.showLogs(someLambdaFunction)
will open the terminal with logs)
- Developers will be able to interact with their cloud account, update payment details, invite users to a project, etc. This allows us to roll-out complex features quickly (the web UI requires significantly more work)
- Note: There is more work that will go into making this experience great. For example,
x9.preval.expr.typed
is leading the way into automatic type generation. So developers could eventually do something like this x9.dev.projects.
… and get autocompletion
- x9.preval and macros
- x9.preval is powerful on its own. But it also serves a number of hidden purposes
- It is a gentle introdution to a programming model where some parts of your code, even within the same file, behave differently and execute at a different time (build time, bundle time, runtime). This is called “multi-stage” programming in general
- This type of paradigm change is similar to the one that we will later introduce when we move to “multi-runtime programming”, which adds an additional dimension (some code is now running on a mobile app, the next line in the server).
- Introducing it early on forces us to work on two things:
- DX: I believe it can be solved with a combination of visual queues (ex: an icon that helps you understand “things are generated here”, and some animations) and constant feedback. This is new territory. And we can use the early feedback.
- Implementation: Having macros from the get-go forces us to build a much more robust build/bundle system from the beginning.
- Peformance: This code is run constantly, in tandem with the compiler. It needs to compile and run incredibly fast
- Isolation, dependencies, tree-shaking: Macros need to be able to run as soon as the “critical path” of execution is available. Even if they deped on macros on the same file, broken code a few downs later, etc. This is new territory.