Over the past few years Terraform has become the status quo in the Infrastructure as Code (IaC) landscape. It lets developers and DevOps engineers define and provision their infrastructure through writing Terraform configuration files using the HashiCorp Configuration Language (HCL).
But should you use it in 2024? Let’s unpack the main use cases, the pros and cons of Terraform, and its implications on developer experience and cloud cost management.
At its core, Terraform is an open-source tool used to build, modify, and version control infrastructure. It supports several service providers, enabling the creation of customized deployments. Terraform operates on the principle of Infrastructure as Code (IaC), where infrastructure is declared as code. This means it can be shared, reviewed, and controlled just like any other software codebase.
Encore Cloud is a modern alternative, aimed at teams looking for an integrated and developer-experience focused approach to backend development. Encore is purposefully designed to make it less complex to build event-driven and distributed systems, and solves for both the local dev experience and assists with deployment using robust and scalable services from AWS and GCP.
It works by providing a Backend Framework that lets developers declare infrastructure semantics as part of the application code. Encore then parses the code and creates a fully type-safe model of your application's microservices, APIs, and infrastructure requirements. Encore then automatically runs your local environment, deploys to temporary preview environments for testing, and provisions infrastructure and deploys to your cloud (AWS/GCP).
In the following examples we're using Encore's TypeScript Framework, you can also refer to the docs to see examples from Encore's Framework for Go.
With Encore you define a service by creating a folder and inside that folder defining an API within a regular TypeScript file. Encore recognizes this as a service, and uses the folder name as the service name. When deploying, Encore will automatically provision the required infrastructure for each service.
On disk it might look like this:
/my-app
├── encore.app // ... and other top-level project files
├── package.json
│
├── hello // hello service (a folder)
│ ├── hello.ts // hello service code
│ └── hello_test.ts // tests for hello service
│
└── world // world service (a folder)
└── world.ts // world service code
This means building a microservices architecture is as simple as creating multiple directories within your application.
Encore lets you easily define type-safe, idiomatic TypeScript API endpoints.
It's simple to accept both the URL path parameters, as well as JSON request body data, HTTP headers, and query strings.
It's done in a way that is fully declarative, enabling Encore to automatically parse and validate the incoming request and ensure it matches the schema, with zero boilerplate.
To define an API, use the api
function from the encore.dev/api
module to wrap a regular TypeScript async function that receives the request data as input and returns response data.
This tells Encore that the function is an API endpoint. Encore will then automatically generate the necessary boilerplate at compile-time.
In the example below, we define the API endpoint ping
which accepts POST
requests and is exposed as hello.ping
(because our service name is hello
).
// inside the hello.ts file
import { api } from "encore.dev/api"
export const ping = api(
{ method: "POST" },
async (p: PingParams): Promise<PingResponse> => {
return { message: `Hello ${p.name}!` };
}
);
Publishers & Subscribers (Pub/Sub) let you build systems that communicate by broadcasting events asynchronously. This is a great way to decouple services for better reliability and responsiveness.
Encore's Backend Framework for Go lets you use Pub/Sub in a cloud-agnostic declarative fashion. At deployment, Encore automatically provisions the required infrastructure.
The core of Pub/Sub is the Topic, a named channel on which you publish events.
Encore makes it very simple to create Topics. For example, here's how you create a topic with events about user signups:
import { Topic } "encore.dev/pubsub"
export interface SignupEvent {
userID: string;
}
export const signups = new Topic<SignupEvent>("signups", {
deliveryGuarantee: "at-least-once",
});
To publish an Event, call publish
on the topic passing in the event object (which is the type specified in the new Topic<Type>
constructor):
const messageID = await signups.publish({userID: id});
// If we get here the event has been successfully published,
// and all registered subscribers will receive the event.
// The messageID variable contains the unique id of the message,
// which is also provided to the subscribers when processing the event.
AWS CloudFormation is an IaC service specifically designed for AWS. It uses YAML or JSON templates to create and manage AWS resources.
Organizations that are heavily invested in AWS and require native integration and management of AWS resources.
Google Cloud Deployment Manager is an IaC tool specifically for Google Cloud Platform, using YAML-based configuration.
Organizations using Google Cloud Platform exclusively, looking for an integrated solution to manage their resources.
Azure Resource Manager is designed for Microsoft's Azure platform, offering robust management of Azure resources through JSON-based templates.
Businesses committed to Azure, seeking a powerful tool to orchestrate and manage their Azure resources effectively.
Terraform undoubtedly brings several benefits to the table, from multi-cloud support to enforcing consistent environments. However, the manual and complex nature of its configuration, the gap between application developers and DevOps, and cost considerations may make alternatives worth considering. Understanding the trade-offs is essential in making an informed choice that suits your project's needs and budget constraints.