diff --git a/docs/source/api/composer.rst b/docs/source/api/composer.rst
index 667b0de60f9564e98de46b562e0fcc0afe7cf2a1..8d3cdce4ac64bd9c689acd127ab03eb32e9b4598 100644
--- a/docs/source/api/composer.rst
+++ b/docs/source/api/composer.rst
@@ -2,35 +2,44 @@
 Composer API (``pystencilssfg.composer``)
 *****************************************
 
-.. autoclass:: pystencilssfg.composer.SfgComposer
+.. module:: pystencilssfg.composer
+
+.. autoclass:: SfgComposer
     :members:
 
-.. autoclass:: pystencilssfg.composer.SfgIComposer
+.. autoclass:: SfgIComposer
     :members:
 
-.. autoclass:: pystencilssfg.composer.SfgBasicComposer
+.. autoclass:: SfgBasicComposer
     :members:
 
-.. autoclass:: pystencilssfg.composer.SfgClassComposer
+.. autoclass:: SfgClassComposer
     :members:
 
 Custom Generators
 =================
 
-.. autoclass:: pystencilssfg.composer.custom.CustomGenerator
+.. module:: pystencilssfg.composer.custom
+
+.. autoclass:: CustomGenerator
     :members:
 
 
 Helper Methods and Builders
 ===========================
 
-.. autofunction:: pystencilssfg.composer.make_sequence
+.. module:: pystencilssfg.composer.basic_composer
+
+.. autofunction:: make_sequence
+
+.. autoclass:: SfgFunctionSequencer
+    :members:
 
-.. autoclass:: pystencilssfg.composer.basic_composer.SfgNodeBuilder
+.. autoclass:: SfgNodeBuilder
     :members:
 
-.. autoclass:: pystencilssfg.composer.basic_composer.SfgBranchBuilder
+.. autoclass:: SfgBranchBuilder
     :members:
 
-.. autoclass:: pystencilssfg.composer.basic_composer.SfgSwitchBuilder
+.. autoclass:: SfgSwitchBuilder
     :members:
diff --git a/docs/source/index.md b/docs/source/index.md
index 4b60903beee1426937961c3c107d584ac755a13d..9ffc4f0c547f181565496e9054447645fc8e767c 100644
--- a/docs/source/index.md
+++ b/docs/source/index.md
@@ -35,8 +35,9 @@ getting_started
 :maxdepth: 1
 :caption: User Guide
 
-usage/generator_scripts
+usage/composer
 C++ API Modelling <usage/api_modelling>
+usage/config_and_cli
 usage/project_integration
 usage/tips_n_tricks
 ```
diff --git a/docs/source/usage/composer.md b/docs/source/usage/composer.md
new file mode 100644
index 0000000000000000000000000000000000000000..fa96539ef95ee75a2b71ce90646c3283d0d820d4
--- /dev/null
+++ b/docs/source/usage/composer.md
@@ -0,0 +1,88 @@
+---
+file_format: mystnb
+kernelspec:
+  name: python3
+---
+
+(composer_guide)=
+# How To Use the Composer API
+
+```{code-cell} ipython3
+:tags: [remove-cell]
+
+import sys
+from pathlib import Path
+
+mockup_path = Path("../_util").resolve()
+sys.path.append(str(mockup_path))
+
+from sfg_monkeypatch import DocsPatchedGenerator  # monkeypatch SFG for docs
+
+from pystencilssfg import SourceFileGenerator
+```
+
+The *composer API* is the interface by which C++ code is constructed in pystencils-sfg.
+It is exposed through the ubiquitous *composer object* returned by the `SourceFileGenerator`
+upon entry into its managed region.
+This guide is meant to illustrate the various constructions possible through the composer,
+starting from things as simple as `#include` directives and plain code strings,
+up to entire classes and their members.
+
+## Basic Functionality
+
+### Prelude Comment
+
+You can equip your generated files with a prelude comment that will be printed at their very top:
+
+```{code-cell} ipython3
+import datetime
+
+now = datetime.datetime.now()
+
+with SourceFileGenerator() as sfg:
+    sfg.prelude(f"This file was generated using pystencils-sfg at {now}.")
+```
+
+### `#include` Directives
+
+Use `sfg.include` to add `#include` directives to your generated files.
+For a system-header include, delimit the header name with `<>`.
+If the directive should be printed not into the header, but the implementation file,
+set `private = True`:
+
+```{code-cell} ipython3
+with SourceFileGenerator() as sfg:
+    sfg.include("my_header.hpp")
+    sfg.include("<memory>")
+    sfg.include("detail_header.hpp", private=True)
+```
+
+### Plain Code Strings
+
+It is always possible to print out plain code strings verbatim.
+Use `sfg.code()` to write code directly to the generated header file.
+To emit the code to the implementation file instead, use `sfg.code(..., impl=True)`.
+
+```{code-cell} ipython3
+with SourceFileGenerator() as sfg:
+    sfg.code("int THE_ANSWER;")
+    sfg.code("int THE_ANSWER = 42;", impl=True)
+```
+
+## Defining Functions
+
+Free functions can be declared and defined using the `sfg.function` sequencer.
+It uses *builder syntax* to declare the various properties of the function in arbitrary
+order via a sequence of calls. This sequence must end with a plain pair of parentheses `( ... )`
+within which the function body will be defined.
+For example, the following will create a function `getValue` with return type `int32` which is marked with the `nodiscard`
+attribute:
+
+```{code-cell} ipython3
+with SourceFileGenerator() as sfg:
+    sfg.function("getValue").returns("int32").attr("nodiscard")(
+        "return 42;"
+    )
+```
+
+For a list of all possible function qualifiers, see the reference of {any}`SfgFunctionSequencer`.
diff --git a/docs/source/usage/generator_scripts.md b/docs/source/usage/config_and_cli.md
similarity index 100%
rename from docs/source/usage/generator_scripts.md
rename to docs/source/usage/config_and_cli.md
diff --git a/src/pystencilssfg/composer/basic_composer.py b/src/pystencilssfg/composer/basic_composer.py
index e75f0e29e4d41825a8d0d8cf1547bab046c1731a..58c28e7efcc73ba9c0c3469a8d31b4336406a849 100644
--- a/src/pystencilssfg/composer/basic_composer.py
+++ b/src/pystencilssfg/composer/basic_composer.py
@@ -7,9 +7,9 @@ from warnings import warn
 
 from pystencils import Field, CreateKernelConfig, create_kernel
 from pystencils.codegen import Kernel
-from pystencils.types import create_type, UserTypeSpec
+from pystencils.types import create_type, UserTypeSpec, PsType
 
-from ..context import SfgContext
+from ..context import SfgContext, SfgCursor
 from .custom import CustomGenerator
 from ..ir import (
     SfgCallTreeNode,
@@ -171,7 +171,7 @@ class SfgBasicComposer(SfgIComposer):
             else:
                 f.prelude += content + end
 
-    def code(self, *code: str):
+    def code(self, *code: str, impl: bool = False):
         """Add arbitrary lines of code to the generated header file.
 
         :Example:
@@ -188,9 +188,15 @@ class SfgBasicComposer(SfgIComposer):
                 #define PI 3.14 // more than enough for engineers
                 using namespace std;
 
+        Args:
+            code: Sequence of code strings to be written to the output file
+            impl: If `True`, write the code to the implementation file; otherwise, to the header file.
         """
         for c in code:
-            self._cursor.write_header(c)
+            if impl:
+                self._cursor.write_impl(c)
+            else:
+                self._cursor.write_header(c)
 
     def define(self, *definitions: str):
         from warnings import warn
@@ -318,10 +324,8 @@ class SfgBasicComposer(SfgIComposer):
     def function(
         self,
         name: str,
-        returns: UserTypeSpec = void,
-        inline: bool = False,
         return_type: UserTypeSpec | None = None,
-    ):
+    ) -> SfgFunctionSequencer:
         """Add a function.
 
         The syntax of this function adder uses a chain of two calls to mimic C++ syntax:
@@ -334,33 +338,17 @@ class SfgBasicComposer(SfgIComposer):
 
         The function body is constructed via sequencing (see `make_sequence`).
         """
+        seq = SfgFunctionSequencer(self._cursor, name)
+
         if return_type is not None:
             warn(
                 "The parameter `return_type` to `function()` is deprecated and will be removed by version 0.1. "
-                "Setting it will override the value of the `returns` parameter. "
-                "Use `returns` instead.",
+                "Use `.returns()` instead.",
                 FutureWarning,
             )
-            returns = return_type
+            seq.returns(return_type)
 
-        def sequencer(*args: SequencerArg):
-            tree = make_sequence(*args)
-            func = SfgFunction(
-                name,
-                self._cursor.current_namespace,
-                tree,
-                return_type=create_type(returns),
-                inline=inline,
-            )
-            self._cursor.add_entity(func)
-
-            if inline:
-                self._cursor.write_header(SfgEntityDef(func))
-            else:
-                self._cursor.write_header(SfgEntityDecl(func))
-                self._cursor.write_impl(SfgEntityDef(func))
-
-        return sequencer
+        return seq
 
     def call(self, kernel_handle: SfgKernelHandle) -> SfgCallTreeNode:
         """Use inside a function body to directly call a kernel.
@@ -621,6 +609,77 @@ def make_sequence(*args: SequencerArg) -> SfgSequence:
     return SfgSequence(children)
 
 
+class SfgFunctionSequencer:
+    """Sequencer for constructing free functions.
+
+    This builder uses call sequencing to specify the function's properties.
+
+    Example:
+
+    >>> sfg.function(
+    ...         "myFunction"
+    ...     ).returns(
+    ...         "float32"
+    ...     ).attr(
+    ...         "nodiscard", "maybe_unused"
+    ...     ).inline().constexpr()(
+    ...         "return 31.2;"
+    ...     )
+    """
+
+    def __init__(self, cursor: SfgCursor, name: str) -> None:
+        self._cursor = cursor
+        self._name = name
+        self._return_type: PsType = void
+
+        #   Qualifiers
+        self._inline: bool = False
+        self._constexpr: bool = False
+
+        #   Attributes
+        self._attributes: list[str] = []
+
+    def returns(self, rtype: UserTypeSpec) -> SfgFunctionSequencer:
+        """Set the return type of the function"""
+        self._return_type = create_type(rtype)
+        return self
+
+    def inline(self) -> SfgFunctionSequencer:
+        """Mark this function as ``inline``."""
+        self._inline = True
+        return self
+
+    def constexpr(self) -> SfgFunctionSequencer:
+        """Mark this function as ``constexpr``."""
+        self._constexpr = True
+        return self
+
+    def attr(self, *attrs: str) -> SfgFunctionSequencer:
+        """Add attributes to this function"""
+        self._attributes += attrs
+        return self
+
+    def __call__(self, *args: SequencerArg) -> None:
+        """Populate the function body"""
+        tree = make_sequence(*args)
+        func = SfgFunction(
+            self._name,
+            self._cursor.current_namespace,
+            tree,
+            return_type=self._return_type,
+            inline=self._inline,
+            constexpr=self._constexpr,
+            attributes=self._attributes,
+        )
+        self._cursor.add_entity(func)
+
+        if self._inline:
+            self._cursor.write_header(SfgEntityDef(func))
+        else:
+            self._cursor.write_header(SfgEntityDecl(func))
+            self._cursor.write_impl(SfgEntityDef(func))
+
+
 class SfgBranchBuilder(SfgNodeBuilder):
     """Multi-call builder for C++ ``if/else`` statements."""
 
diff --git a/src/pystencilssfg/emission/file_printer.py b/src/pystencilssfg/emission/file_printer.py
index 6ab98eb89aba143dd567fc0c176199ff9d1444fe..dea6605b10dddc792df316eefc50c40d01a73126 100644
--- a/src/pystencilssfg/emission/file_printer.py
+++ b/src/pystencilssfg/emission/file_printer.py
@@ -34,16 +34,16 @@ class SfgFilePrinter:
     def __call__(self, file: SfgSourceFile) -> str:
         code = ""
 
-        if file.file_type == SfgSourceFileType.HEADER:
-            code += "#pragma once\n\n"
-
         if file.prelude:
             comment = "/**\n"
-            comment += indent(file.prelude, " * ")
+            comment += indent(file.prelude, " * ", predicate=lambda _: True)
             comment += " */\n\n"
 
             code += comment
 
+        if file.file_type == SfgSourceFileType.HEADER:
+            code += "#pragma once\n\n"
+
         for header in file.includes:
             incl = str(header) if header.system_header else f'"{str(header)}"'
             code += f"#include {incl}\n"
@@ -148,7 +148,9 @@ class SfgFilePrinter:
                     code += f" {defined_entity.owning_class.name}::"
                 code += f" {name}"
                 if defined_entity.default_init is not None:
-                    args_str = ", ".join(str(expr) for expr in defined_entity.default_init)
+                    args_str = ", ".join(
+                        str(expr) for expr in defined_entity.default_init
+                    )
                     code += "{" + args_str + "}"
                 code += ";"
                 return code
@@ -177,8 +179,19 @@ class SfgFilePrinter:
 
     def _func_signature(self, func: SfgFunction | SfgMethod, inclass: bool):
         code = ""
+
+        if func.attributes:
+            code += "[[" + ", ".join(func.attributes) + "]]"
+
         if func.inline:
             code += "inline "
+
+        if isinstance(func, SfgMethod) and func.static:
+            code += "static "
+
+        if func.constexpr:
+            code += "constexpr "
+
         code += func.return_type.c_string() + " "
         params_str = ", ".join(
             f"{param.dtype.c_string()} {param.name}" for param in func.parameters
diff --git a/src/pystencilssfg/ir/entities.py b/src/pystencilssfg/ir/entities.py
index 62ae1eb7611065c7b1a12b77d6894143aed341dd..d97e81022e611c9e3ac87ba14e69a8f6ebdfb46b 100644
--- a/src/pystencilssfg/ir/entities.py
+++ b/src/pystencilssfg/ir/entities.py
@@ -1,5 +1,6 @@
 from __future__ import annotations
 
+from dataclasses import dataclass
 from abc import ABC
 from enum import Enum, auto
 from typing import (
@@ -203,10 +204,20 @@ class SfgKernelNamespace(SfgNamespace):
         self._kernels[kernel.name] = kernel
 
 
-class SfgFunction(SfgCodeEntity):
+@dataclass(frozen=True)
+class CommonFunctionProperties:
+    tree: SfgCallTreeNode
+    parameters: tuple[SfgVar, ...]
+    return_type: PsType
+    inline: bool
+    constexpr: bool
+    attributes: Sequence[str]
+
+
+class SfgFunction(SfgCodeEntity, CommonFunctionProperties):
     """A free function."""
 
-    __match_args__ = ("name", "tree", "parameters", "return_type")
+    __match_args__ = ("name", "tree", "parameters", "return_type")  # type: ignore
 
     def __init__(
         self,
@@ -215,37 +226,27 @@ class SfgFunction(SfgCodeEntity):
         tree: SfgCallTreeNode,
         return_type: PsType = void,
         inline: bool = False,
+        constexpr: bool = False,
+        attributes: Sequence[str] = (),
     ):
         super().__init__(name, namespace)
 
-        self._tree = tree
-        self._return_type = return_type
-        self._inline = inline
-
-        self._parameters: tuple[SfgVar, ...]
-
         from .postprocessing import CallTreePostProcessing
 
         param_collector = CallTreePostProcessing()
-        self._parameters = tuple(
-            sorted(param_collector(self._tree).function_params, key=lambda p: p.name)
+        parameters = tuple(
+            sorted(param_collector(tree).function_params, key=lambda p: p.name)
         )
 
-    @property
-    def parameters(self) -> tuple[SfgVar, ...]:
-        return self._parameters
-
-    @property
-    def tree(self) -> SfgCallTreeNode:
-        return self._tree
-
-    @property
-    def return_type(self) -> PsType:
-        return self._return_type
-
-    @property
-    def inline(self) -> bool:
-        return self._inline
+        CommonFunctionProperties.__init__(
+            self,
+            tree,
+            parameters,
+            return_type,
+            inline,
+            constexpr,
+            attributes,
+        )
 
 
 class SfgVisibility(Enum):
@@ -323,10 +324,10 @@ class SfgMemberVariable(SfgVar, SfgClassMember):
         return self._default_init
 
 
-class SfgMethod(SfgClassMember):
+class SfgMethod(SfgClassMember, CommonFunctionProperties):
     """Instance method of a class"""
 
-    __match_args__ = ("name", "tree", "parameters", "return_type")
+    __match_args__ = ("name", "tree", "parameters", "return_type")  # type: ignore
 
     def __init__(
         self,
@@ -336,43 +337,35 @@ class SfgMethod(SfgClassMember):
         return_type: PsType = void,
         inline: bool = False,
         const: bool = False,
+        static: bool = False,
+        constexpr: bool = False,
+        attributes: Sequence[str] = (),
     ):
         super().__init__(cls)
 
-        self._name = name
-        self._tree = tree
-        self._return_type = return_type
-        self._inline = inline
-        self._const = const
-
-        self._parameters: tuple[SfgVar, ...]
-
         from .postprocessing import CallTreePostProcessing
 
         param_collector = CallTreePostProcessing()
-        self._parameters = tuple(
-            sorted(param_collector(self._tree).function_params, key=lambda p: p.name)
+        parameters = tuple(
+            sorted(param_collector(tree).function_params, key=lambda p: p.name)
         )
 
-    @property
-    def name(self) -> str:
-        return self._name
-
-    @property
-    def parameters(self) -> tuple[SfgVar, ...]:
-        return self._parameters
-
-    @property
-    def tree(self) -> SfgCallTreeNode:
-        return self._tree
+        self._static = static
+        self._const = const
 
-    @property
-    def return_type(self) -> PsType:
-        return self._return_type
+        CommonFunctionProperties.__init__(
+            self,
+            tree,
+            parameters,
+            return_type,
+            inline,
+            constexpr,
+            attributes,
+        )
 
     @property
-    def inline(self) -> bool:
-        return self._inline
+    def static(self) -> bool:
+        return self._static
 
     @property
     def const(self) -> bool: