diff --git a/docs/usage/building.md b/docs/usage/building.md
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..01d129500861dfe25b9bb18152d2ac73842a004e 100644
--- a/docs/usage/building.md
+++ b/docs/usage/building.md
@@ -0,0 +1,30 @@
+
+## Namespaces
+
+Conceptually, there exist two different kinds of namespaces: *kernel namespaces* for the generated kernels,
+and a single *code namespace* for all the generated code.
+Both get mapped to standard C++ namespaces, in the end, but they fulfill different purposes in the code generator.
+
+*Kernel namespaces* are used for grouping generated kernels together, e.g. to avoid name collisions.
+If, for example, a code generation script combines kernels and functions produced by different components, each
+component may create its own kernel namespace to isolate its kernels.
+
+The *code namespace*, in contrast, envelops all the generated code. Its fully qualified name is built from two parts:
+
+ - The *outer namespace* is defined in the [generator configuration][pystencilssfg.SfgConfiguration], typically by
+   the global project configuration;
+ - The *inner namespace* is defined by the code generation script, e.g. via [`SfgComposer.namespace`][pystencilssfg.SfgComposer.namespace].
+
+These namespaces will finally occur in the generated implementation file as:
+
+```C++
+namespace outer_namespace::inner_namespace {
+
+namespace kernels {
+    /* kernel definitions */
+} // namespace kernels
+
+/* function definitions */
+
+} // namespace outer_namespace::inner_namespace
+```
diff --git a/integration/CMakeDemo/codegen_config.py b/integration/CMakeDemo/codegen_config.py
index 67f309315f3b6bb95146d4be4a0e40dbd6b57d0e..40a70f91c70e0d9cec2a5f748bd002a99f875395 100644
--- a/integration/CMakeDemo/codegen_config.py
+++ b/integration/CMakeDemo/codegen_config.py
@@ -11,6 +11,6 @@ def sfg_config():
     return SfgConfiguration(
         header_extension='hpp',
         source_extension='cpp',
-        base_namespace='cmake_demo',
+        outer_namespace='cmake_demo',
         project_info=project_info
     )
diff --git a/integration/MakeDemo/kernels.py b/integration/MakeDemo/kernels.py
index dc790d2da4f4ae6df74dfb6fe54db7563d7d8080..add435f06bc5ecd454c1472a5f2632efe242603f 100644
--- a/integration/MakeDemo/kernels.py
+++ b/integration/MakeDemo/kernels.py
@@ -1,11 +1,19 @@
+# type: ignore
+
 import sympy as sp
 
 from pystencils import fields, kernel
 
-from pystencilssfg import SourceFileGenerator
+from pystencilssfg import SourceFileGenerator, SfgConfiguration
 from pystencilssfg.source_concepts.cpp import mdspan_ref
 
-with SourceFileGenerator() as sfg:
+sfg_config = SfgConfiguration(
+    outer_namespace="make_demo"
+)
+
+with SourceFileGenerator(sfg_config) as sfg:
+    sfg.namespace("jacobi")
+
     u_src, u_dst, f = fields("u_src, u_dst, f(1) : double[2D]", layout="fzyx")
     h = sp.Symbol("h")
 
diff --git a/integration/MakeDemo/main.cpp b/integration/MakeDemo/main.cpp
index 21c801572b71267b86a08a56782cd40ddeb4ae81..033261f0f090d0e6ed1bfeec2d999e29a7ac8e90 100644
--- a/integration/MakeDemo/main.cpp
+++ b/integration/MakeDemo/main.cpp
@@ -39,7 +39,7 @@ int main(int argc, char ** argv){
     }
     
     for(uint32_t i = 0; i < n_iters; ++i){
-        jacobi_smooth(f, h, dst, src);
+        make_demo::jacobi::jacobi_smooth(f, h, dst, src);
         std::swap(src, dst);
     }
 
diff --git a/src/pystencilssfg/composer.py b/src/pystencilssfg/composer.py
index f44979803ea2b211e9df7dd399359d07142a7940..8d2c9766b697517dfe1dae1921b53ecf0efd0007 100644
--- a/src/pystencilssfg/composer.py
+++ b/src/pystencilssfg/composer.py
@@ -24,6 +24,10 @@ class SfgComposer:
     @property
     def context(self):
         return self._ctx
+    
+    def namespace(self, namespace: str):
+        """Set the inner code namespace. Throws an exception if a namespace was already set."""
+        self._ctx.set_namespace(namespace)
 
     @property
     def kernels(self) -> SfgKernelNamespace:
diff --git a/src/pystencilssfg/configuration.py b/src/pystencilssfg/configuration.py
index b93478aef8106dbc1adf07936a8118d780157303..355be5e4c4d7bdd1a06724de253de9732971e30a 100644
--- a/src/pystencilssfg/configuration.py
+++ b/src/pystencilssfg/configuration.py
@@ -55,7 +55,7 @@ class SfgConfiguration:
     header_only: bool | None = None
     """If set to `True`, generate only a header file without accompaning source file."""
 
-    base_namespace: str | None = None
+    outer_namespace: str | None = None
     """The outermost namespace in the generated file. May be a valid C++ nested namespace qualifier
     (like `a::b::c`) or `None` if no outer namespace should be generated."""
 
@@ -88,7 +88,7 @@ DEFAULT_CONFIG = SfgConfiguration(
     header_extension='h',
     source_extension='cpp',
     header_only=False,
-    base_namespace=None,
+    outer_namespace=None,
     codestyle=SfgCodeStyle(),
     output_directory=""
 )
diff --git a/src/pystencilssfg/context.py b/src/pystencilssfg/context.py
index 0c083831b4095e4552176b11a3cfa676da719e98..548d9e5727b642a6dfb2e75b3947420b8156b766 100644
--- a/src/pystencilssfg/context.py
+++ b/src/pystencilssfg/context.py
@@ -15,7 +15,9 @@ class SfgContext:
         self._code_namespace = None
 
         #   Source Components
+        self._prelude: list[str] = []
         self._includes: set[SfgHeaderInclude] = set()
+        self._definitions: list[str] = []
         self._kernel_namespaces = {self._default_kernel_namespace.name: self._default_kernel_namespace}
         self._functions: dict[str, SfgFunction] = dict()
 
@@ -31,8 +33,8 @@ class SfgContext:
         return self._argv
 
     @property
-    def root_namespace(self) -> str | None:
-        return self._config.base_namespace
+    def outer_namespace(self) -> str | None:
+        return self._config.outer_namespace
 
     @property
     def inner_namespace(self) -> str | None:
@@ -40,7 +42,7 @@ class SfgContext:
 
     @property
     def fully_qualified_namespace(self) -> str | None:
-        match (self.root_namespace, self.inner_namespace):
+        match (self.outer_namespace, self.inner_namespace):
             case None, None: return None
             case outer, None: return outer
             case None, inner: return inner
@@ -53,15 +55,37 @@ class SfgContext:
         return self._config.codestyle
 
     # ----------------------------------------------------------------------------------------------
-    #   Kernel Namespaces
+    #   Prelude, Includes, Definitions, Namespace
     # ----------------------------------------------------------------------------------------------
 
+    def prelude_comments(self) -> Generator[str, None, None]:
+        """The prelude is a comment block printed at the top of both generated files."""
+        yield from self._prelude
+    
+    def append_to_prelude(self, code_str: str):
+        self._prelude.append(code_str)
+
     def includes(self) -> Generator[SfgHeaderInclude, None, None]:
+        """Includes of headers. Public includes are added to the header file, private includes
+        are added to the implementation file."""
         yield from self._includes
 
     def add_include(self, include: SfgHeaderInclude):
         self._includes.add(include)
 
+    def definitions(self) -> Generator[str, None, None]:
+        """Definitions are code lines printed at the top of the header file, after the includes."""
+        yield from self._definitions
+
+    def add_definition(self, definition: str):
+        self._definitions.append(definition)
+
+    def set_namespace(self, namespace: str):
+        if self._code_namespace is not None:
+            raise SfgException("The code namespace was already set.")
+        
+        self._code_namespace = namespace
+
     # ----------------------------------------------------------------------------------------------
     #   Kernel Namespaces
     # ----------------------------------------------------------------------------------------------