The Demise of Jinja in Data Transformation
Interlace has fundamentally rethought data transformation by eliminating traditional templating languages like Jinja. Instead of using {{ }} or {% %} syntax, Interlace models are defined directly in .sql files containing SQL, or .py files containing functions. This approach treats SQL itself as executable code, moving away from the abstraction layer provided by templating engines. The initial reaction to this shift might be that capabilities are lost, especially for users accustomed to Jinja's power in tools like dbt. However, Interlace argues that Jinja was performing four distinct, unrelated jobs, each of which can be addressed more effectively with dedicated solutions when SQL is treated as an Abstract Syntax Tree (AST).
The core philosophy shift is to leverage the structure of SQL itself. By parsing SQL into an AST, tools can understand the code's intent, dependencies, and relationships programmatically. This allows for more robust and type-safe operations compared to string-based templating, which can be prone to errors and difficult to debug. Interlace's approach aims to bring software engineering best practices to data transformation pipelines.

Job One: Declaring Dependencies with Expressions
The first job Jinja typically handles is declaring dependencies between data models, often through functions like ref() in dbt. Interlace replaces this with a more direct, expression-based approach. Instead of a Jinja macro calling a function that's interpreted at pre-processing time, Interlace directly embeds dependency information within the SQL itself. When a model needs to reference another, it's expressed as a valid SQL construct that the Interlace compiler understands as a link. This is akin to how a compiled programming language handles function calls or variable references. The dependency isn't a string that gets substituted; it's a structural element of the SQL query that the system resolves.
This method offers several advantages. Firstly, it makes the dependency graph explicit and directly visible within the SQL code. Developers can read a SQL file and immediately understand which other models it relies on, without needing to cross-reference Jinja templates. Secondly, by treating dependencies as SQL expressions, Interlace can perform static analysis on the entire dependency graph before execution. This allows for early detection of circular dependencies, missing models, or incorrect model references, which are common pain points in complex data pipelines. The system can also optimize query execution plans more effectively when it has a clear, programmatically accessible understanding of model relationships.
Job Two: Parameterization and Configuration
The second major function Jinja serves is parameterization and configuration. This involves injecting variables, environment-specific settings, or dynamic values into SQL queries. For example, a date filter might be set using a Jinja variable that changes based on the execution environment. Interlace tackles this by treating these configurations as parameters to SQL expressions or functions. Instead of a Jinja template rendering a string with a variable, Interlace models can accept explicit parameters. These parameters are then used within the SQL query in a type-safe manner. This is analogous to how stored procedures in databases accept parameters, ensuring that the data types are correct and that the SQL remains valid.
This approach brings robustness to configuration. When a parameter is passed to an Interlace model, the system can validate its type and format against the expected schema. This prevents runtime errors that can occur when Jinja injects a malformed string into a SQL query. Furthermore, it separates configuration logic from SQL logic more cleanly. Configuration values can be managed externally, perhaps through environment variables or configuration files, and then passed into the SQL execution context. This makes pipelines easier to manage, test, and deploy across different environments, from development to production.
Job Three: Dynamic SQL Generation
Jinja is also frequently used for dynamic SQL generation, where code blocks or entire query structures are conditionally included or modified based on logic. This can range from adding optional `WHERE` clauses to constructing complex `CASE` statements. Interlace replaces this with SQL's own procedural capabilities or by structuring the problem differently. If dynamic SQL is truly needed, it can be achieved through SQL's native procedural extensions (like PL/pgSQL or T-SQL) or by composing smaller, well-defined SQL models. Interlace's AST-based system can then analyze these composed models and their relationships.
The key here is to avoid generating SQL as strings. When SQL is generated dynamically as text, it becomes difficult to parse, validate, and secure. It opens the door to SQL injection vulnerabilities if not handled with extreme care. By contrast, Interlace encourages building SQL from structured components. If a complex conditional is needed, it might be represented as a separate, parameterized SQL model that can be called by other models. This makes the logic modular, testable, and auditable. The system can understand the structure of the generated SQL because it's not arbitrary text; it's a composition of known, analyzable SQL constructs.

Job Four: Code Reusability and Macros
The fourth job Jinja performs is code reusability, often through macros that encapsulate common SQL patterns. Users define a macro once and call it multiple times throughout their project. Interlace's updated approach, incorporating macros as SQL expressions, addresses this. These are not Jinja-like string-substitution macros but rather SQL constructs that can be parameterized and invoked. Think of them as user-defined functions within the SQL dialect that Interlace supports. When a macro is called, it's treated as an expression that resolves to a specific SQL fragment or subquery, which is then integrated into the larger AST.
This makes reusability more robust and semantically meaningful. A SQL macro in Interlace has a defined signature (inputs and outputs) and operates within the SQL type system. This is more akin to how functions work in compiled languages. When you call a macro, you're not just substituting text; you're invoking a piece of logic that the system understands and can optimize. This improves code quality, reduces errors, and makes the overall data transformation process more maintainable. The distinction between a SQL expression-based macro and a Jinja string-based macro is critical: the former is integrated into the AST, while the latter is a pre-processing step that generates raw SQL strings.
The Future of Data Transformation: SQL as Code
Interlace's decision to move away from Jinja and embrace SQL as an AST represents a significant step towards treating data pipelines with the same rigor as traditional software development. By breaking down the monolithic function of Jinja into distinct, better-solved problems—dependency declaration, parameterization, dynamic generation, and reusability—Interlace offers a more robust, maintainable, and developer-friendly platform. This paradigm shift simplifies workflows, enhances debugging, and ultimately leads to more reliable data transformations. The move implies that as SQL capabilities evolve and are better understood by tooling, templating languages become increasingly obsolete for data transformation tasks.
