GithubHelp home page GithubHelp logo

blueprint-uservices / blueprint Goto Github PK

View Code? Open in Web Editor NEW
12.0 0.0 2.0 1.78 MB

Blueprint Microservices Compiler: Flexible and Configurable Open-Source Microservice Benchmark Applications

Home Page: https://blueprint-uservices.github.io

License: BSD 3-Clause "New" or "Revised" License

Go 99.65% Shell 0.06% Python 0.29%
compiler experimentation microservices microservices-application microservices-demo microservices-example research

blueprint's People

Contributors

jonathanmace avatar vaastav avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

docc-lab

blueprint's Issues

Implement basic NoSQLDatabase - [merged]

In GitLab by @JonathanMace on Sep 29, 2023, 02:05

Merges simplenosqldb -> main

Implements a simple standalone nosql database that supports a limited subset of mongodb query language.

Since the implementation is entirely in-memory, the projection argument to some API calls is ignored.

Update operations are not implemented yet.

Closes #37

[Docker] Deploy to Docker container

In GitLab by @JonathanMace on Sep 16, 2023, 01:34

Port the implementation of Docker containers.

Docker container will be a namespace that contains one or more process nodes.

Ideally the IR implementation will be a simpler version of the goproc namespace implementation. The main concerns will be collecting and packaging artifacts from the contained processes, including software dependencies like installing Go; getting the cmd line instantiation strings; and ensuring that argnodes are passed from outside the container to the processes, presumably through environment variables.

Extract some common codegen templating functions into gogen - [merged]

In GitLab by @JonathanMace on Sep 20, 2023, 04:25

Merges templatefuncs -> main

This extracts some common codegen templating functions into the gogen package. Concretely convenience functions for enumerating arguments and return variables from gocode.Func objects.

Also updates the usage of these helper functions in the grpc plugin and workloadgen plugin.

[Goproc] Add a service modifier that wraps a service in a goproc

In GitLab by @JonathanMace on Sep 16, 2023, 24:57

Currently, a wiring spec can deploy a service into a goproc in one of two ways:

First, it can be explicitly deployed and instantiated:

goproc.CreateProcess(wiring, procName, serviceName)
bp := wiring.GetBlueprint()
bp.Instantiate(procName)

Second, it can be implicitly instantiated if some other node depends on it:

b := workflow.Define(wiring, "b", "LeafServiceImpl")
a := workflow.Define(wiring, "a", "NonLeafService", b)
goproc.CreateProcess(wiring, "proca", a)
bp := wiring.GetBlueprint()
bp.Instantiate("proca")

In the latter case, b is not explicitly instantiated, but because a depends on b, the result is that b will be implicitly instantiated within the same process as a.

Ideally, we should be able to apply modifiers to services to define their default instantiation behavior. For example, there could be a modifier for b that implements logic akin to "If b is instantiated, do so in its own process".

Implementing this is fairly straightforward. It would require a new IRNode dst modifier that creates a process before instantiating the rest of the pointer. It would also require additional helper methods in the goproc plugin for adding the "deploy to a process" modifier to services.

[Core] Improve compiler logging with callstacks

In GitLab by @JonathanMace on Sep 16, 2023, 01:06

The compiler's approach to logging things during compilation needs to be improved as follows:

  • All logging statements should include the source file and line number. Some logging statements, such as those generated within the core Blueprint components, might want to include the caller source file and line number, to provide a bit more detail.
  • We should use an errors package that includes the callstack when an error is generated, rather than fmt.Errorf. When an error is generated and logged, we should print the full callstack of the error.
  • Currently, whenever the wiring spec defines a node, we save the wiring spec origin as a callsite property. This additional information can be used to enrich our logging, because it can tie back compilation steps to the specific wiring line that defined nodes.
  • Similar to above, we could embed more information in the generated code, as comments, to say where this generated code came from.

[Workflow] Workflow services should support configuration values

In GitLab by @JonathanMace on Sep 16, 2023, 24:43

Although #2 describes a more general approach to configuration, it would also be nice to more directly support configuration values in workflow services as follows.

For example, we might wish to define a workflow service that takes a configuration value:

func NewNonLeafServiceImpl(leafService LeafService, myConfigValue string) (NonLeafService, error)

In the wiring spec, workflow services are instantiated with workflow.Define. The arguments of workflow.Define must name IRNodes to be used as arguments to the above constructor.

However, while #3 does suggest that we introduce an IRNode to represent configuration values, it should not be required by default for workflows, as it introduces verbosity into the wiring spec. In the simple case it would be nice to be able to directly provide configuration values in the call to wiring.Define, e.g.

wiring.Define(workflow, "myservice", "NonLeafServiceImpl", "myleafservice", "myconfigvalue")

For the above to succeed, we will need to eagerly load the constructor definition of NonLeafServiceImpl and look at the argument types to deduce that the "myleafservice" argument should correspond to an IRNode, whereas "myconfigvalue" is a basic type and thus should be inserted directly into the generated code.

The workflow IRNode will thus need to be updated to save those config values, and the code generation updated to insert the config values.

[Housekeeping] Add GetGoInterface to golang.Service and update usage by plugins

In GitLab by @JonathanMace on Sep 19, 2023, 20:12

Too many plugins are doing this verbose nonsense:

	service, valid := node.Wrapped.GetInterface().(*gocode.ServiceInterface)
	if !valid {
		return fmt.Errorf("expected %v to have a gocode.ServiceInterface but got %v",
			node.Name(), node.Wrapped.GetInterface())
	}

golang.Service nodes are required for GetInterface to return a *gocode.ServiceInterface. Instead of the above, let's just extend the golang.Service interface with a method GetGoInterface *gocode.ServiceInterface. It'll make the code cleaner and easier for plugins to use, and it will catch type errors at IR-build time instead of compilation time.

After adding this method, we will need to update the IR nodes for a few of the plugins too, to implement this method.

[GRPC] Handling of pointers and optional arguments

In GitLab by @JonathanMace on Sep 16, 2023, 24:31

The current GRPC plugin implementation is a 'golden case' implementation that only handles structs and questionably handles pointers. It is untested what will happen for methods that take pointer types.

Implementing support for pointers is relatively straightforward:
1 For an RPC method argument, a pointer type should translate into an optional GRPC message field.
2 In the marshall and demarshall code, there should be nil checking for pointer types.

[Clientpool] Implement Clientpool plugin

In GitLab by @JonathanMace on Sep 16, 2023, 01:29

The main trickiness in porting the Clientpool plugin is to implement the Namespace IR node that represents the plugin. It will need to use the DI graph builder similar to how the goproc plugin does, and then clients are created by invoking the generated graph method multiple times, once for each client.

[Golang] Remove redundant dependency tracking for module building

In GitLab by @JonathanMace on Sep 18, 2023, 20:17

See #6 for a discussion.

When generating Golang modules, we do not need to explicitly keep track of which dependencies are needed for inclusion in the go.mod file. It is enough to use go mod tidy to have Go automatically determine the module dependencies to include.

Currently, the Golang module builder does track modules and explicitly constructs the go.mod file. We can simplify the code by removing this logic.

One caveat is unpublished modules -- a module might depend on a sibling module on the local filesystem that cannot be resolved using its import URL. In this case the go.mod file needs a replace directive to point to the sibling directory. Currently, the Golang workspace builder adds this replace directive. The replace directive will be need before we can invoke go mod tidy. This should be a minor tweak.

This change will also result in some code being removed from plugins, which are currently explicitly adding the required modules to the modulebuilder; now they don't need to do that at all.

[Core] Rename ptr src and dst to client and server

In GitLab by @JonathanMace on Sep 19, 2023, 20:29

Currently, pointers use src and dst to refer to the caller and callee side of things respectively. Rename this to client and server. Although client and server is actually more specific than what pointers can represent, the terminology is more clear than src and dst.

[Housekeeping] Add a convenience method to golang.ModuleBuilder to create a package

In GitLab by @JonathanMace on Sep 19, 2023, 20:19

It seems multiple plugins are creating package subdirectories in the module, leading to code repetition such as:

	splits := strings.Split(outputPackage, "/")
	outputPackageName := splits[len(splits)-1]
	client.PackageName = builder.Info().Name + "/" + outputPackage
	client.PackageShortName = outputPackageName

	outputDir := filepath.Join(builder.Info().Path, filepath.Join(splits...))
	err := os.MkdirAll(outputDir, 0755)
	if err != nil {
		return fmt.Errorf("unable to create grpc output dir %v due to %v", outputDir, err.Error())
	}

We could add a method to golang.ModuleBuilder called something like CreatePackage that implements the above logic and returns some sort of Info struct containing package name, package shortname, package directory; and an error.

This would simplify things in a number of plugins. We would want to update the code of those plugins to use the new method.

[Applications] Port and test Sock Shop

In GitLab by @JonathanMace on Sep 16, 2023, 01:11

Blueprint v2 workflow specs are backwards compatible with Blueprint v1, but wiring specs need to be rewritten.

Once Blueprint v2 supports the set of plugins required by Sock Shop, rewrite its wiring spec for Blueprint v2.

[Core] Accumulate errors in blueprint.WiringSpec

In GitLab by @JonathanMace on Sep 20, 2023, 22:42

Some wiring spec methods and convenience methods might return errors, but it is tedious and verbose to expect a wiring spec to check error return values for all wiring spec calls.

The golang suggested alternative approach can be used here; we can accumulate errors in the blueprint.WiringSpec. Then to do error checking, the wiring spec can provide a method Err() to return any accumulated error(s). For wiring specs, this means they can do error checking, but without verbose error checking.

[Logging] Implement logging for services

In GitLab by @JonathanMace on Sep 16, 2023, 01:45

Should provide a way to configure Logging in the wiring spec to configure various logging backends; but instead of injecting logging frameworks in the constructor, they should be accessible via a getter.

Initial code generation interfaces and implementations. - [merged]

In GitLab by @JonathanMace on Sep 7, 2023, 04:07

Merges mace_wip -> main

Substantial code reorganization and implementation of code-generation parts. The code builds and runs but some pieces are still unimplemented; notably the GRPC code generation is partially complete (still needs wrapper structs and instantiation code) and the tracing wrappers are unimplemented. However, the hairiest bits -- AST parsing for workflow spec and GRPC struct serialization -- are in and working.

[Core] Configuration IR Node

In GitLab by @JonathanMace on Sep 16, 2023, 24:06

Currently, configuration of components is done by plugins in a relatively manual way, as described in #2

This issue proposes to introduce a configuration IRNode to Blueprint core. The IRNode would be similar to the Address IRNode, and it can either be used directly by plugins or extended.

Wiring and IR

The typical usage of a configuration IRNode would be to define the node at application scope. The configuration node can have a specific name that could either be given by the plugin opaquely, or explicitly given by the user (both are permissible). For example, for RPC timeout, a node would be created with a name "grpc.timeout" or something equivalent. Any other IRNode that depends on the configuration would have to Get the node.

Runtime

Namespace nodes would need to support config nodes in the generated code. Currently, namespace nodes must support the general idea of an argument being passed in, and the current implementation is simple -- it assumes anything passed in is (a) required; and (b) comes as a string:

func checkArg(name, value string) {
	if value == "" {
		slog.Error("No value set for required cmd line argument " + name)
		os.Exit(1)
	} else {
		slog.Info(fmt.Sprintf("Arg %v = %v", name, value))
	}
}

func main() {
	a_grpc_addr := flag.String("a.grpc.addr", "", "Argument automatically generated from Blueprint IR: a.grpc.addr = GolangServerAddress()")
	b_grpc_addr := flag.String("b.grpc.addr", "", "Argument automatically generated from Blueprint IR: b.grpc.addr = GolangServerAddress()")
	
	flag.Parse()

	checkArg("a.grpc.addr", *a_grpc_addr)
	checkArg("b.grpc.addr", *b_grpc_addr)
	
	graphArgs := map[string]string{
		"a.grpc.addr": *a_grpc_addr,
		"b.grpc.addr": *b_grpc_addr,
	}

	// .....
}

The code snippet above is generated by the goproc plugin, and it would need to change in a couple of ways. During code generation, the plugin would typecheck all argNodes to see if they are of type configuration. If they are a configuration node, then they would be handled differently in the generated code. Unlike is currently implemented, a configuration node might be specified as required or optional (currently all args are required). And, it might be given a default value, which would be put into the flag.String declaration (implying it is optional).

Special-casing

The above approach would enable us to selectively override configuration. For example, we could deploy all GRPC servers for all GRPC services with the same configuration value "grpc.timeout", but perhaps for a specific service "serviceB" we decide to deploy GRPC with a different timeout value, so we define a different configuration value "b.grpc.timeout".

[Core] Support cyclical dependencies in Wiring spec and generated Golang code

In GitLab by @JonathanMace on Sep 16, 2023, 24:47

The current Wiring spec implementation will recurse infinitely if there are cyclical node dependencies. Similarly, the golang DI graph would recurse infinitely if there were cyclical dependencies.

This is not a fundamental limitation. However, to support cyclical node dependencies might entail tweaking the way IR nodes are constructed, to have constructor injection as well as setter-based injection.

This will be a major change.

Golang.module builder.create package - [merged]

In GitLab by @JonathanMace on Sep 20, 2023, 04:52

Merges golang.ModuleBuilder.CreatePackage -> main

Implements #28.

Creates a method CreatePackage on the ModuleBuilder that takes care of tedious filepath combining and directory creation.

Updates 3 plugins to use CreatePackage: GRPC server, GRPC client, workloadgen

[Applications] Port and test DSB Social Network

In GitLab by @JonathanMace on Sep 16, 2023, 01:10

Blueprint v2 workflow specs are backwards compatible with Blueprint v1, but wiring specs need to be rewritten.

Once Blueprint v2 supports the set of plugins required by DSB SN, rewrite the wiring spec for Blueprint v2.

[Workflow] Support multiple constructors

In GitLab by @JonathanMace on Sep 16, 2023, 24:49

It is reasonable for a workflow spec to provide multiple constructors for a service, e.g. with expanded configuration options.

The current workflow spec parsing logic does not support multiple constructors, but there is no fundamental reason preventing us from doing so.

One option is to allow the workflow spec to state exactly which constructor to invoke

Another option is to pattern-match the constructor based on the args provided in the wiring spec.

[Docker] Generate docker-compose file

In GitLab by @JonathanMace on Sep 16, 2023, 01:36

The docker-compose file is a namespace one level up from generating the docker containers. It should be implemented as a separate plugin with separate IRNode representations.

Since the docker containers themselves do most of the heavy lifting in terms of packaging artifacts, the main concern of the docker-compose plugin will be to ensure that containers are instantiated and that arguments are passed between containers correctly.

[Goproc] Invoke go build after workspace generation

In GitLab by @JonathanMace on Sep 16, 2023, 24:51

After generating a golang workspace, the plugin should invoke go build in order to check if the workspace builds successfully. In general we would expect the workspace to build successfully, but perhaps if there is a bug in a plugin, it might not build successfully. It is desirable to eagerly surface this information to users during the compilation stage, rather than compile code that won't run.

[Golang] Auto-generated workload client

In GitLab by @JonathanMace on Sep 16, 2023, 24:22

This issue proposes a goworkload plugin that generates a go module that directly calls services of an application.

Background

Blueprintv1 contains an auto-generated workload client; this is not yet implemented in Blueprintv2. The closest we currently get is in the goproc plugin, which provides the wiring helper function goproc.CreateClientProcess; this will package all dependencies and generate a main method that instantiates a client. To get a workload we then need to manually extend this code to invoke methods on the client.

This approach is appropriate for users who want to implement custom workloads, but it is tedious during application and plugin development, when interfaces will be in flux and recompilation commonplace

Proposal

The goworkload plugin will generate code that is similar to the goproc plugin. It lets the user specify which service to build a workload client for, but does the following in addition:

1 Has additional command line parameter to choose which function to call. Possibly we could also let the workload contain clients for multiple services, in which case we would have a command line parameter to also choose which service to call
2 Invokes the specified service/function once, printing the output or error. Uses default values for arguments to the service. As a possible extension, we could expose all of the arguments as command line parameters (this might be unnecessarily tedious).

Suggested implementation

There is a neat way of implementing this without duplicating any goproc code. The plugin can be implemented as a golang node with a run method. The plugin can optionally expose config values (See #3 ) for choosing which function to invoke. The run method makes the calls then exits. The goproc generated code would execute this completely transparently, and would exit once the run method completes. The generated workload code would simply look something like this:

type NonLeafService_WorkloadGenerator struct {
	Service leaf.NonLeafService
}

func New_NonLeafService_WorkloadGenerator(service leaf.NonLeafService) (*NonLeafService_WorkloadGenerator, error) {
	workload := &NonLeafService_WorkloadGenerator{}
	workload.Service = service
	return workload, nil
}

// Blueprint: Run is called automatically in a separate goroutine by runtime/plugins/golang/di.go
func (workload *NonLeafService_WorkloadGenerator) Run(ctx context.Context) error {
	fmt.Println("Invoking leaf.NonLeafService.Hello")
	ret0, err := workload.Service.Hello(ctx, 55)
	if err != nil {
		fmt.Printf("Error: %s\n", err.Error())
	} else {
		fmt.Printf("Received %v\n", ret0)
	}
	return err
}

Possible complication

For services with complex struct arguments, it's possible that pointers within the struct cause problems by defaulting to nil. The GRPC plugin doesn't yet safely handle pointers in structs.

[Core] Configuring Components

In GitLab by @JonathanMace on Sep 15, 2023, 21:34

Components can have configuration associated with them. For example, RPC plugins can have a timeout associated with them. Similarly a clientpool modifier will have a configuration value for the number of clients in the clientpool.

The current approach to configuration is direct and does not support a number of use cases. This issue is to incorporate a general notion of configuration into Blueprint and covers:

1 Introducing new IR nodes to represent configuration
1 Exposing configuration to users through the wiring spec
1 Providing configuration APIs to plugins that wish to offer more complex configuration than the current direct approach

Background: Current Approach

Configuration is currently possible without any additional support from core Blueprint components. However, it only supports a limited use-case: plugins can offer configuration only by directly exposing it to users in the wiring spec.

Currently, plugins define their own helper methods for adding nodes to the wiring spec. For example, the GRPC plugin has the helper method grpc.Deploy(wiring blueprint.WiringSpec, serviceName string) that will deploy a service using GRPC.

If a plugin wishes to expose configuration options in the wiring spec, it can do the following:

1 Simple and direct: A plugin can provide multiple helper methods, to allow for extra configuration options. For example, to expose a Timeout option, the plugin could also expose a method grpc.DeployWithTimeout(wiring blueprint.WiringSpec, serviceName string, timeout int)
2 More general approach: If a plugin provides multiple options, then it can define an options struct and provide an additional method that takes the option struct, e.g. grpc.DeployWithOptions(wiring blueprint.WiringSpec, serviceName string, options GRPCOptions); any options set in the struct will be used, otherwise defaults will be used

The above approach does not change the plugin's IR nodes substantially. Now, all they need to do is store the configuration values on the existing IR node(s).

The above approach also does not substantially affect a plugin's code generation. The existing code generation would just be expanded to take into account any configuration values on the IR node. The generated code would directly bake in the config values.

Limitations of Current Approach

The current approach fails the following use-cases

1 Workflow spec nodes, specifically, do not support configuration values, because the workflow spec plugin does not support it
2 Plugins that support the same concepts -- e.g. Thrift and gRPC both have timeouts -- are configured in arbitrarily different ways; potentially they could use the same configuration transparently (thus requiring fewer wiring spec changes when swapping between plugins)
3 Users cannot override configuration values at runtime
4 In the generated system, configuration is not centralized in any way -- values are dispersed into code arbitrarily, making it difficult for users assess the configuration by looking at the generated code

Proposed Approach

The sub-issues related to this issue cover the various implementation pieces related to configuration for Blueprint and for specific plugins.

Roadmap

In GitLab by @JonathanMace on Aug 23, 2023, 04:05

This issue outlines the concrete work associated with the upcoming Blueprint milestones

Milestone 1: Barebones Deployment with GRPC

Workflow spec services that can be deployed over GRPC, thereby running in separate processes. Manually start the processes and submit a workload on the same machine.

Plugins required:

  • Workflow Spec Services Plugin
  • Golang Process Plugin
  • GRPC Plugin

Detailed task list:

  • Golang Process Plugin
    • Tidying
      • Update DICodeBuilder and Impl to add an ImportType method for convenience. Remove the current Import implementation on DICodeBuilder and replace its usage with the gogen.Imports struct. Simplify template generation
      • Update the workflow spec plugin implementation to use the above, simplifying the implementation. Also move codegen parts into a codegen subdir
    • Generated DI code
      • Generated graph() method must declare all string args in di. It should return the container (with Get method), not graph.
    • DI runtime graph/container
      • container Get method should receive a context so that things like server goroutines can shutdown on process exit
      • Container should have a waitgroup so that callers can wait on things like server goroutines
      • Add an Await method on the DI container to wait for the waitgroup
    • Generated main method
      • Main-method generation that takes cmd line args, puts them in a map, and invokes the graph function
      • Process IRNode should keep track of which nodes must be explicitly instantiated. Generated main() should do that instantiation by calling get() after the definitions.
      • Generated main method should create a context for cancellation, pass it to the DI container, and await the waitgroup after getting all definitions
  • GRPC Plugin
    • Client and Server Handler
      • Finish implementing generated client methods; see if using copier is sufficient
      • Finish implementing generated server methods
      • Implement instantiation code for client, making use of the ctx above
      • Implement instantiation code for server, making use of the ctx and waitgroup above
    • Configuration
      • Add a general IRNode representing runtime configuration -- ie specifying that this config should come from runtime
      • Add config for timeout, allowing both a runtime configured value, or a directly configured value

Milestone 2: Minimally Featured Deployment

Workflow spec services that can be wrapped with tracing and clientpools, can be deployed over GRPC, and can utilize memcached. Processes are deployed in docker containers and a docker-compose file is created. With these plugins implemented, we should have touched all the various kinds of plugins and concepts currently considered by Blueprint.

Plugins required:

  • OpenTelemetry w/ Jaeger backend
  • Clientpools
  • Runtime stdlib + Cache
  • Memcached
  • Docker container image
  • Docker compose

Detailed task list:

  • TODO: many tasks associated with the above

Milestone 3: Release-level

Plugins required:

  • Databases, etc. and initializing them (e.g. with schemas), MongoDB
  • Metrics collection, log collection
  • HTTP frontend

Milestone 4: Fully-featured parity with Blueprint v1

Plugins required:

  • Kubernetes deployment
  • Manual replication and load balancing
  • Platform Replication and load balancing using kubernetes
  • Circuit breaker prototype
  • Async / queue-based services, RabbitMQ
  • Configurable RPC retries
  • other DBs, e.g. MySQL
  • other caches, e.g. Redis
  • georeplication

Milestone 5: wishlist

Plugins required:

  • storage backend, e.g. HDFS, Cassandra
  • Sidecar pattern + Envoy
  • other queues, e.g. Kafka
  • Thrift support
  • cloud deployment

Other functionality:

  • workloads
  • fault injection

Additional:

Desirable tasks:

  • Rename Scope to Namespace in code, comments, and docs
  • Rename pointer, src, and dst to something else
  • General approach to testing, even if not fully done
  • Example applications
  • Documentation and tutorials

[Webserver] Implement HTTP frontend

In GitLab by @JonathanMace on Sep 16, 2023, 01:40

Implementing the HTTP webserver frontend will follow a similar pattern to the GRPC plugin, except the marshalling and unmarshalling will just be directly to string and included in the HTTP payload.

[Golang] Cache builtin package list

In GitLab by @JonathanMace on Sep 16, 2023, 24:29

Currently the golang/gocode/typename.go loads the list of builtin package names as follows:

func initBuiltins() {
	if len(builtins) > 0 {
		return
	}
	pkgs, err := packages.Load(nil, "std")
	if err != nil {
		panic(err)
	}

	for _, p := range pkgs {
		builtins[p.PkgPath] = struct{}{}
	}
}

This is slow (a few seconds). The list of builtin package names only changes when go versions change. We should just extract this list rather than loading them dynamically every time. A minor annoyance.

[OpenTelemetry] OpenTelemetry plugin implementation porting

In GitLab by @JonathanMace on Sep 16, 2023, 01:25

Finish porting the OpenTelemetry plugin to Blueprint v2.

Among other things this includes:

  • Generating the client and server wrapper code
  • Deploying the OT collector process

Unlike in the Blueprint v1 code, the Tracer should be received as argument to the wrapper code, rather than created with each RPC call.

[Core] Wiring spec tests

In GitLab by @JonathanMace on Sep 16, 2023, 01:08

Investigate how to implement "test" wiring specs that compile particular features or configurations. The tests should primarily check to see if compilation succeeds, and the resulting code builds, rather than fully running the resulting code. Fully running the resulting code (ie integration tests) should come next.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.