diff --git a/.github/workflows/CompatHelper.yml b/.github/workflows/CompatHelper.yml
index 369f4b915..2afd0558c 100644
--- a/.github/workflows/CompatHelper.yml
+++ b/.github/workflows/CompatHelper.yml
@@ -3,10 +3,29 @@ on:
   schedule:
     - cron: 0 0 * * *
   workflow_dispatch:
+permissions:
+  contents: write
+  pull-requests: write
 jobs:
   CompatHelper:
     runs-on: ubuntu-latest
     steps:
+      - name: Check if Julia is already available in the PATH
+        id: julia_in_path
+        run: which julia
+        continue-on-error: true
+      - name: Install Julia, but only if it is not already available in the PATH
+        uses: julia-actions/setup-julia@v1
+        with:
+          version: '1'
+          arch: ${{ runner.arch }}
+        if: steps.julia_in_path.outcome != 'success'
+      - name: "Add the General registry via Git"
+        run: |
+          import Pkg
+          ENV["JULIA_PKG_SERVER"] = ""
+          Pkg.Registry.add("General")
+        shell: julia --color=yes {0}
       - name: "Install CompatHelper"
         run: |
           import Pkg
diff --git a/Project.toml b/Project.toml
index 5c0dfc3cd..ac3b50af2 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,11 +1,12 @@
 name = "COBREXA"
 uuid = "babc4406-5200-4a30-9033-bf5ae714c842"
 authors = ["The developers of COBREXA.jl"]
-version = "1.4.0"
+version = "1.4.1"
 
 [deps]
 Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
 DistributedData = "f6a0035f-c5ac-4ad0-b410-ad102ced35df"
+DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
 HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f"
 JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
 JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
@@ -22,27 +23,28 @@ StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3"
 Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
 
 [compat]
+Clarabel = "0.3"
 DistributedData = "0.1.4, 0.2"
+DocStringExtensions = "0.8, 0.9"
 HDF5 = "0.16"
 JSON = "0.21"
-JuMP = "0.21.0, 0.22.0, 0.23, 1"
+JuMP = "1"
 MAT = "0.10"
 MacroTools = "0.5.6"
-OSQP = "0.6"
 OrderedCollections = "1.4"
-SBML = "~1.1"
+SBML = "~1.3"
 StableRNGs = "1.0"
 Tulip = "0.7.0, 0.8.0, 0.9.2"
 julia = "1.5"
 
 [extras]
 Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
+Clarabel = "61c947e1-3e6d-4ee4-985a-eec8c727bd6e"
 Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
 GLPK = "60bf3e95-4087-53dc-ae20-288a0d20c6a6"
-OSQP = "ab2f91bb-94b4-55e3-9ba0-7f65df51de79"
 SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce"
 Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
 Tulip = "6dd1b50a-3aae-11e9-10b5-ef983d2400fa"
 
 [targets]
-test = ["Aqua", "Downloads", "GLPK", "OSQP", "SHA", "Test", "Tulip"]
+test = ["Aqua", "Clarabel", "Downloads", "GLPK", "SHA", "Test", "Tulip"]
diff --git a/README.md b/README.md
index 0ec26b778..47237b005 100644
--- a/README.md
+++ b/README.md
@@ -84,7 +84,7 @@ add COBREXA
 ```
 
 You also need to install your favorite solver supported by `JuMP.jl` (such as
-Gurobi, Mosek, CPLEX, GLPK, OSQP, etc., see a [list
+Gurobi, Mosek, CPLEX, GLPK, Clarabel, etc., see a [list
 here](https://jump.dev/JuMP.jl/stable/installation/#Supported-solvers)).  For
 example, you can install `Tulip.jl` solver by typing:
 ```
diff --git a/docs/Project.toml b/docs/Project.toml
index 6d1d4c31c..1f523ccd8 100644
--- a/docs/Project.toml
+++ b/docs/Project.toml
@@ -1,6 +1,7 @@
 [deps]
 COBREXA = "babc4406-5200-4a30-9033-bf5ae714c842"
 CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
+Clarabel = "61c947e1-3e6d-4ee4-985a-eec8c727bd6e"
 Clustering = "aaaa29a8-35af-508c-8bc3-b662a17a0fe5"
 ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
 Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
@@ -10,7 +11,6 @@ InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
 JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
 JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
 Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
-OSQP = "ab2f91bb-94b4-55e3-9ba0-7f65df51de79"
 Tulip = "6dd1b50a-3aae-11e9-10b5-ef983d2400fa"
 
 [compat]
diff --git a/docs/src/examples/05b_fba_mods.jl b/docs/src/examples/05b_fba_mods.jl
index a8027a6c1..8beff7a04 100644
--- a/docs/src/examples/05b_fba_mods.jl
+++ b/docs/src/examples/05b_fba_mods.jl
@@ -10,7 +10,7 @@
 !isfile("e_coli_core.xml") &&
     download("http://bigg.ucsd.edu/static/models/e_coli_core.xml", "e_coli_core.xml")
 
-using COBREXA, GLPK, Tulip
+using COBREXA, GLPK, Tulip, JuMP
 
 model = load_model("e_coli_core.xml")
 
@@ -33,6 +33,6 @@ fluxes = flux_balance_analysis_dict(
         knockout(["b0978", "b0734"]), # knock out two genes
         change_optimizer(Tulip.Optimizer), # ignore the above optimizer and switch to Tulip
         change_optimizer_attribute("IPM_IterationsLimit", 1000), # customize Tulip
-        change_sense(MAX_SENSE), # explicitly tell Tulip to maximize the objective
+        change_sense(JuMP.MAX_SENSE), # explicitly tell Tulip to maximize the objective
     ],
 )
diff --git a/docs/src/examples/08_pfba.jl b/docs/src/examples/08_pfba.jl
index 6ca3a2579..c10fbfd02 100644
--- a/docs/src/examples/08_pfba.jl
+++ b/docs/src/examples/08_pfba.jl
@@ -13,7 +13,7 @@
 !isfile("e_coli_core.xml") &&
     download("http://bigg.ucsd.edu/static/models/e_coli_core.xml", "e_coli_core.xml")
 
-using COBREXA, Tulip, OSQP
+using COBREXA, Tulip, Clarabel
 
 model = load_model("e_coli_core.xml")
 
@@ -21,13 +21,13 @@ model = load_model("e_coli_core.xml")
 # capable of solving quadratic programs.
 #
 # As the simplest choice, we can use
-# [`OSQP.jl`](https://osqp.org/docs/get_started/julia.html), but any any
+# [`Clarabel.jl`](https://osqp.org/docs/get_started/julia.html), but any any
 # [`JuMP.jl`-supported
 # optimizer](https://jump.dev/JuMP.jl/stable/installation/#Supported-solvers)
 # that supports quadratic programming will work.
 
-#md # !!! note "Note: OSQP can be sensitive"
-#md #       We recommend reading the documentation of `OSQP` before using it, since
+#md # !!! note "Note: Clarabel can be sensitive"
+#md #       We recommend reading the documentation of `Clarabel` before using it, since
 #md #       it may give inconsistent results depending on what settings
 #md #       you use. Commercial solvers like `Gurobi`, `Mosek`, `CPLEX`, etc.
 #md #       require less user engagement.
@@ -40,10 +40,9 @@ model = load_model("e_coli_core.xml")
 
 fluxes = parsimonious_flux_balance_analysis_dict(
     model,
-    OSQP.Optimizer;
+    Clarabel.Optimizer;
     modifications = [
-        silence, # optionally silence the optimizer (OSQP is very verbose by default)
-        change_optimizer_attribute("polish", true), # tell OSQP to invest time into improving the precision of the solution
+        silence, # optionally silence the optimizer (Clarabel is very verbose by default)
         change_constraint("R_EX_glc__D_e"; lb = -12, ub = -12), # fix glucose consumption rate
     ],
 )
@@ -69,8 +68,7 @@ flux_vector = parsimonious_flux_balance_analysis_vec(
         change_optimizer_attribute("IPM_IterationsLimit", 500), # we may change Tulip-specific attributes here
     ],
     qp_modifications = [
-        change_optimizer(OSQP.Optimizer), # now switch to OSQP (Tulip wouldn't be able to finish the computation)
-        change_optimizer_attribute("polish", true), # get an accurate solution, see OSQP's documentation
+        change_optimizer(Clarabel.Optimizer), # now switch to Clarabel (Tulip wouldn't be able to finish the computation)
         silence, # and make it quiet.
     ],
 )
diff --git a/docs/src/examples/13_moma.jl b/docs/src/examples/13_moma.jl
index c84463c16..200ccd07d 100644
--- a/docs/src/examples/13_moma.jl
+++ b/docs/src/examples/13_moma.jl
@@ -19,17 +19,14 @@ using COBREXA
 
 model = load_model(StandardModel, "e_coli_core.xml")
 
-# MOMA analysis requires solution of a quadratic model, we will thus use OSQP as the main optimizer.
+# MOMA analysis requires solution of a quadratic model, we will thus use Clarabel as the main optimizer.
 
-using OSQP
+using Clarabel
 
 # We will need a reference solution, which represents the original state of the
 # organism before the change.
-reference_flux = flux_balance_analysis_dict(
-    model,
-    OSQP.Optimizer;
-    modifications = [silence, change_optimizer_attribute("polish", true)],
-)
+reference_flux =
+    flux_balance_analysis_dict(model, Clarabel.Optimizer; modifications = [silence])
 
 # As the change here, we manually knock out CYTBD reaction:
 changed_model = change_bound(model, "R_CYTBD", lower = 0.0, upper = 0.0);
@@ -40,8 +37,8 @@ flux_summary(
     minimize_metabolic_adjustment_analysis_dict(
         changed_model,
         reference_flux,
-        OSQP.Optimizer;
-        modifications = [silence, change_optimizer_attribute("polish", true)],
+        Clarabel.Optimizer;
+        modifications = [silence],
     ),
 )
 
@@ -51,7 +48,7 @@ flux_summary(
 flux_summary(
     flux_balance_analysis_dict(
         changed_model,
-        OSQP.Optimizer;
-        modifications = [silence, change_optimizer_attribute("polish", true)],
+        Clarabel.Optimizer;
+        modifications = [silence],
     ),
 )
diff --git a/docs/src/quickstart.md b/docs/src/quickstart.md
index 50c6f1ca4..11c9709fa 100644
--- a/docs/src/quickstart.md
+++ b/docs/src/quickstart.md
@@ -8,7 +8,7 @@ add COBREXA
 ```
 
 You also need to install your favorite solver supported by `JuMP.jl` (such as
-Gurobi, Mosek, CPLEX, GLPK, OSQP, etc., see a [list
+Gurobi, Mosek, CPLEX, GLPK, Clarabel, etc., see a [list
 here](https://jump.dev/JuMP.jl/stable/installation/#Supported-solvers)).  For
 example, you can install `Tulip.jl` solver by typing:
 ```
diff --git a/src/COBREXA.jl b/src/COBREXA.jl
index c84c4acad..9e75888f9 100644
--- a/src/COBREXA.jl
+++ b/src/COBREXA.jl
@@ -1,3 +1,28 @@
+"""
+```
+\\\\\\\\\\  // //     | COBREXA.jl  v$(COBREXA.COBREXA_VERSION)
+ \\\\ \\\\// //      |
+  \\\\ \\/ //       | COnstraint-Based Reconstruction
+   \\\\  //        | and EXascale Analysis in Julia
+   //  \\\\        |
+  // /\\ \\\\       | See documentation and examples at:
+ // //\\\\ \\\\      | https://lcsb-biocore.github.io/COBREXA.jl
+// //  \\\\\\\\\\     |
+```
+
+To start up quickly, install your favorite optimizer, load a metabolic model in
+a format such as SBML or JSON, and run a metabolic analysis such as the flux
+balance analysis:
+```
+import Pkg; Pkg.add("GLPK")
+using COBREXA, GLPK
+model = load_model("e_coli_core.xml")
+x = flux_balance_analysis_dict(model, GLPK.Optimizer)
+flux_summary(x)
+```
+
+A complete overview of the functionality can be found in the documentation.
+"""
 module COBREXA
 
 using Distributed
@@ -14,12 +39,17 @@ using Serialization
 using SparseArrays
 using StableRNGs
 using Statistics
+using DocStringExtensions
 
 import Base: findfirst, getindex, show
 import Pkg
 import SBML # conflict with Reaction struct name
 
-include("banner.jl")
+const _PKG_ROOT_DIR = normpath(joinpath(@__DIR__, ".."))
+include_dependency(joinpath(_PKG_ROOT_DIR, "Project.toml"))
+
+const COBREXA_VERSION =
+    VersionNumber(Pkg.TOML.parsefile(joinpath(_PKG_ROOT_DIR, "Project.toml"))["version"])
 
 # autoloading
 const _inc(path...) = include(joinpath(path...))
@@ -34,6 +64,7 @@ _inc_all.(
             joinpath("base", "macros"),
             joinpath("base", "types"),
             joinpath("base", "types", "wrappers"),
+            joinpath("base", "ontologies"),
             "base",
             "io",
             joinpath("io", "show"),
diff --git a/src/analysis/envelopes.jl b/src/analysis/envelopes.jl
index 147d38f11..ddf22bc09 100644
--- a/src/analysis/envelopes.jl
+++ b/src/analysis/envelopes.jl
@@ -1,6 +1,6 @@
 
 """
-    envelope_lattice(model::MetabolicModel, rids::Vector{String}; kwargs...)
+$(TYPEDSIGNATURES)
 
 Version of [`envelope_lattice`](@ref) that works on string reaction IDs instead
 of integer indexes.
@@ -9,13 +9,7 @@ envelope_lattice(model::MetabolicModel, rids::Vector{String}; kwargs...) =
     envelope_lattice(model, Vector{Int}(indexin(rids, reactions(model))); kwargs...)
 
 """
-    envelope_lattice(
-        model::MetabolicModel,
-        ridxs::Vector{Int};
-        samples = 10,
-        ranges = collect(zip(bounds(model)...))[ridxs],
-        reaction_samples = fill(samples, length(ridxs)),
-    )
+$(TYPEDSIGNATURES)
 
 Create a lattice (list of "tick" vectors) for reactions at indexes `ridxs` in a
 model. Arguments `samples`, `ranges`, and `reaction_samples` may be optionally
@@ -33,7 +27,7 @@ envelope_lattice(
 )
 
 """
-    objective_envelope(model::MetabolicModel, rids::Vector{String}, args...; kwargs...)
+$(TYPEDSIGNATURES)
 
 Version of [`objective_envelope`](@ref) that works on string reaction IDs
 instead of integer indexes.
@@ -47,16 +41,7 @@ objective_envelope(model::MetabolicModel, rids::Vector{String}, args...; kwargs.
     )
 
 """
-    objective_envelope(
-        model::MetabolicModel,
-        ridxs::Vector{Int},
-        optimizer;
-        modifications = [],
-        lattice_args = (),
-        lattice = envelope_lattice(model, ridxs; lattice_args...),
-        analysis = screen_optimize_objective,
-        kwargs...,
-    )
+$(TYPEDSIGNATURES)
 
 Compute an array of objective values for the `model` for rates of reactions
 specified `ridxs` fixed to a regular range of values between their respective
diff --git a/src/analysis/flux_balance_analysis.jl b/src/analysis/flux_balance_analysis.jl
index 25a534b08..495f4545f 100644
--- a/src/analysis/flux_balance_analysis.jl
+++ b/src/analysis/flux_balance_analysis.jl
@@ -1,5 +1,5 @@
 """
-    flux_balance_analysis_vec(model::MetabolicModel, args...)::Maybe{Vector{Float64}}
+$(TYPEDSIGNATURES)
 
 A variant of FBA that returns a vector of fluxes in the same order as reactions
 of the model, if the solution is found.
@@ -17,7 +17,7 @@ flux_balance_analysis_vec(
     flux_vector(model, flux_balance_analysis(model, args...; kwargs...))
 
 """
-    flux_balance_analysis_dict(model::MetabolicModel, args...)::Maybe{Dict{String, Float64}}
+$(TYPEDSIGNATURES)
 
 A variant of FBA that returns a dictionary assigning fluxes to reactions, if
 the solution is found. Arguments are passed to [`flux_balance_analysis`](@ref).
@@ -33,11 +33,7 @@ flux_balance_analysis_dict(
     flux_dict(model, flux_balance_analysis(model, args...; kwargs...))
 
 """
-    flux_balance_analysis(
-        model::M,
-        optimizer;
-        modifications = [],
-    ) where {M<:MetabolicModel}
+$(TYPEDSIGNATURES)
 
 Run flux balance analysis (FBA) on the `model` optionally specifying
 `modifications` to the problem.  Basically, FBA solves this optimization problem:
diff --git a/src/analysis/flux_variability_analysis.jl b/src/analysis/flux_variability_analysis.jl
index 7935da1bd..b540ec598 100644
--- a/src/analysis/flux_variability_analysis.jl
+++ b/src/analysis/flux_variability_analysis.jl
@@ -1,14 +1,5 @@
 """
-    flux_variability_analysis(
-        model::MetabolicModel,
-        fluxes::Vector{Int},
-        optimizer;
-        modifications = [],
-        workers = [myid()],
-        optimal_objective_value = nothing,
-        bounds = z -> (z, Inf),
-        ret = objective_value,
-    )::Matrix{Float64}
+$(TYPEDSIGNATURES)
 
 Flux variability analysis solves a pair of optimization problems in `model` for
 each flux `f` described in `fluxes`:
@@ -108,7 +99,7 @@ function flux_variability_analysis(
 end
 
 """
-    flux_variability_analysis(model::MetabolicModel, flux_indexes::Vector{Int}, optimizer; kwargs...)
+$(TYPEDSIGNATURES)
 
 An overload of [`flux_variability_analysis`](@ref) that explores the fluxes specified by integer indexes
 """
@@ -131,11 +122,7 @@ function flux_variability_analysis(
 end
 
 """
-    flux_variability_analysis(
-        model::MetabolicModel,
-        optimizer;
-        kwargs...
-    )
+$(TYPEDSIGNATURES)
 
 A simpler version of [`flux_variability_analysis`](@ref) that maximizes and
 minimizes all declared fluxes in the model. Arguments are forwarded.
@@ -144,12 +131,7 @@ flux_variability_analysis(model::MetabolicModel, optimizer; kwargs...) =
     flux_variability_analysis(model, reaction_flux(model), optimizer; kwargs...)
 
 """
-    flux_variability_analysis_dict(
-        model::MetabolicModel,
-        optimizer;
-        kwargs...
-    )
-
+$(TYPEDSIGNATURES)
 A variant of [`flux_variability_analysis`](@ref) that returns the individual
 maximized and minimized fluxes as two dictionaries (of dictionaries). All
 keyword arguments except `ret` are passed through.
@@ -182,7 +164,7 @@ function flux_variability_analysis_dict(model::MetabolicModel, optimizer; kwargs
 end
 
 """
-    _max_variability_flux(opt_model, flux, sense, ret)
+$(TYPEDSIGNATURES)
 
 Internal helper for maximizing reactions in optimization model.
 """
@@ -194,7 +176,7 @@ function _max_variability_flux(opt_model, flux, sense, ret)
 end
 
 """
-    reaction_variability_analysis(model::MetabolicModel, reaction_indexes::Vector{Int}, optimizer; kwargs...)
+$(TYPEDSIGNATURES)
 
 A variant for [`flux_variability_analysis`](@ref) that examines actual
 reactions (selected by their indexes in `reactions` argument) instead of whole
@@ -226,7 +208,7 @@ function reaction_variability_analysis(
 end
 
 """
-    reaction_variability_analysis( model::MetabolicModel, optimizer; kwargs...)
+$(TYPEDSIGNATURES)
 
 Shortcut for [`reaction_variability_analysis`](@ref) that examines all reactions.
 """
diff --git a/src/analysis/gecko.jl b/src/analysis/gecko.jl
index eedef6208..b4b5dcab7 100644
--- a/src/analysis/gecko.jl
+++ b/src/analysis/gecko.jl
@@ -1,12 +1,5 @@
 """
-    make_gecko_model(
-        model::MetabolicModel;
-        reaction_isozymes::Union{Function,Dict{String,Vector{Isozyme}}}
-        gene_product_bounds::Union{Function,Dict{String,Tuple{Float64,Float64}}},
-        gene_product_molar_mass::Union{Function,Dict{String,Float64}},
-        gene_product_mass_group::Union{Function,Dict{String,String}} = _ -> "uncategorized",
-        gene_product_mass_group_bound::Union{Function,Dict{String,Float64}},
-    )
+$(TYPEDSIGNATURES)
 
 Wrap a model into a [`GeckoModel`](@ref), following the structure given by
 GECKO algorithm (see [`GeckoModel`](@ref) documentation for details).
diff --git a/src/analysis/max_min_driving_force.jl b/src/analysis/max_min_driving_force.jl
index fb3e751c7..8bb20f4eb 100644
--- a/src/analysis/max_min_driving_force.jl
+++ b/src/analysis/max_min_driving_force.jl
@@ -1,24 +1,5 @@
 """
-    max_min_driving_force(
-        model::MetabolicModel,
-        reaction_standard_gibbs_free_energies::Dict{String,Float64},
-        optimizer;
-        flux_solution::Dict{String,Float64} = Dict{String,Float64}(),
-        proton_ids::Vector{String} = ["h_c", "h_e"],
-        water_ids::Vector{String} = ["h2o_c", "h2o_e"],
-        constant_concentrations::Dict{String,Float64} = Dict{String,Float64}(),
-        concentration_ratios::Dict{Tuple{String,String},Float64} = Dict{
-            Tuple{String,String},
-            Float64,
-        }(),
-        concentration_lb = 1e-9,
-        concentration_ub = 100e-3,
-        T = _constants.T,
-        R = _constants.R,
-        small_flux_tol = 1e-6,
-        modifications = [],
-        ignore_reaction_ids = [],
-    )
+$(TYPEDSIGNATURES)
 
 Perform a max-min driving force analysis on the `model`, as defined by Noor, et al.,
 "Pathway thermodynamics highlights kinetic obstacles in central metabolism.", PLoS
@@ -183,29 +164,7 @@ function max_min_driving_force(
 end
 
 """
-    max_min_driving_force_variability(
-        model::MetabolicModel,
-        reaction_standard_gibbs_free_energies::Dict{String,Float64},
-        optimizer;
-        workers =[myid()],
-        optimal_objective_value = nothing,
-        bounds = z -> (z, Inf),
-        flux_solution::Dict{String,Float64} = Dict{String,Float64}(),
-        proton_ids::Vector{String} = ["h_c", "h_e"],
-        water_ids::Vector{String} = ["h2o_c", "h2o_e"],
-        constant_concentrations::Dict{String,Float64} = Dict{String,Float64}(),
-        concentration_ratios::Dict{Tuple{String,String},Float64} = Dict{
-            Tuple{String,String},
-            Float64,
-        }(),
-        concentration_lb = 1e-9,
-        concentration_ub = 100e-3,
-        T = _constants.T,
-        R = _constants.R,
-        small_flux_tol = 1e-6,
-        modifications = [],
-        ignore_reaction_ids = [],
-    )
+$(TYPEDSIGNATURES)
 
 Perform a variant of flux variability analysis on a max min driving force type problem.
 Arguments are forwarded to [`max_min_driving_force`](@ref). Calls [`screen`](@ref)
@@ -256,11 +215,11 @@ function max_min_driving_force_variability(
 
     dgr_variants = [
         [[_mmdf_add_df_bound(lb, ub), _mmdf_dgr_objective(ridx, sense)]] for
-        ridx = 1:n_reactions(model), sense in [MOI.MAX_SENSE, MOI.MIN_SENSE]
+        ridx = 1:n_reactions(model), sense in [MAX_SENSE, MIN_SENSE]
     ]
     concen_variants = [
         [[_mmdf_add_df_bound(lb, ub), _mmdf_concen_objective(midx, sense)]] for
-        midx = 1:n_metabolites(model), sense in [MOI.MAX_SENSE, MOI.MIN_SENSE]
+        midx = 1:n_metabolites(model), sense in [MAX_SENSE, MIN_SENSE]
     ]
 
     return screen(
@@ -278,6 +237,8 @@ function max_min_driving_force_variability(
 end
 
 """
+$(TYPEDSIGNATURES)
+
 Helper function to change the objective to optimizing some dG.
 """
 function _mmdf_dgr_objective(ridx, sense)
@@ -287,6 +248,8 @@ function _mmdf_dgr_objective(ridx, sense)
 end
 
 """
+$(TYPEDSIGNATURES)
+
 Helper function to change the objective to optimizing some concentration.
 """
 function _mmdf_concen_objective(midx, sense)
@@ -296,6 +259,8 @@ function _mmdf_concen_objective(midx, sense)
 end
 
 """
+$(TYPEDSIGNATURES)
+
 Helper function to add a new constraint on the driving force.
 """
 function _mmdf_add_df_bound(lb, ub)
diff --git a/src/analysis/minimize_metabolic_adjustment.jl b/src/analysis/minimize_metabolic_adjustment.jl
index d15501f37..e51e0ea0d 100644
--- a/src/analysis/minimize_metabolic_adjustment.jl
+++ b/src/analysis/minimize_metabolic_adjustment.jl
@@ -1,11 +1,5 @@
 """
-    minimize_metabolic_adjustment_analysis(
-        model::MetabolicModel,
-        flux_ref::Union{Dict{String,Float64}, Vector{Float64}},
-        optimizer;
-        modifications = [],
-        kwargs...
-    )
+$(TYPEDSIGNATURES)
 
 Run minimization of metabolic adjustment (MOMA) on `model` with respect to
 `flux_ref`, which is a vector of fluxes in the order of `reactions(model)`.
@@ -52,7 +46,7 @@ minimize_metabolic_adjustment_analysis(
 )
 
 """
-    minimize_metabolic_adjustment(flux_ref::Vector{Float64})
+$(TYPEDSIGNATURES)
 
 An optimization model modification that implements the MOMA in
 [`minimize_metabolic_adjustment_analysis`](@ref).
@@ -69,7 +63,7 @@ minimize_metabolic_adjustment(flux_ref::Vector{Float64}) =
     end
 
 """
-    minimize_metabolic_adjustment(flux_ref_dict::Dict{String, Float64})
+$(TYPEDSIGNATURES)
 
 Overload of [`minimize_metabolic_adjustment`](@ref) that works with a
 dictionary of fluxes.
@@ -82,7 +76,7 @@ minimize_metabolic_adjustment(flux_ref_dict::Dict{String,Float64}) =
         )
 
 """
-    minimize_metabolic_adjustment_analysis_vec(model::MetabolicModel, args...; kwargs...)
+$(TYPEDSIGNATURES)
 
 Perform minimization of metabolic adjustment (MOMA) and return a vector of fluxes in the
 same order as the reactions in `model`. Arguments are forwarded to
@@ -95,7 +89,7 @@ minimize_metabolic_adjustment_analysis_vec(model::MetabolicModel, args...; kwarg
     flux_vector(model, minimize_metabolic_adjustment_analysis(model, args...; kwargs...))
 
 """
-    minimize_metabolic_adjustment_analysis_dict(model::MetabolicModel, args...; kwargs...)
+$(TYPEDSIGNATURES)
 
 Perform minimization of metabolic adjustment (MOMA) and return a dictionary mapping the
 reaction IDs to fluxes. Arguments are forwarded to [`minimize_metabolic_adjustment`](@ref)
diff --git a/src/analysis/modifications/crowding.jl b/src/analysis/modifications/crowding.jl
index 510f96799..8bad4c23c 100644
--- a/src/analysis/modifications/crowding.jl
+++ b/src/analysis/modifications/crowding.jl
@@ -1,6 +1,6 @@
 
 """
-    add_crowding_constraints(weights::Dict{Int64, Float64})
+$(TYPEDSIGNATURES)
 
 Adds a molecular crowding constraint to the optimization problem: `∑ wᵢ × vᵢ ≤ 1` where `wᵢ`
 is a weight and `vᵢ` is a flux index in the model's reactions specified in `weights` as `vᵢ
@@ -23,7 +23,7 @@ add_crowding_constraints(weights::Dict{Int64,Float64}) =
     end
 
 """
-    add_crowding_constraints(weights::Dict{String, Float64})
+$(TYPEDSIGNATURES)
 
 Variant of [`add_crowding_constraints`](@ref) that takes a dictinary of reactions `ids`
 instead of reaction indices mapped to weights.
diff --git a/src/analysis/modifications/generic.jl b/src/analysis/modifications/generic.jl
index eb8e9dddb..bcf1c0b89 100644
--- a/src/analysis/modifications/generic.jl
+++ b/src/analysis/modifications/generic.jl
@@ -1,6 +1,6 @@
 
 """
-    constrain_objective_value(tolerance)
+$(TYPEDSIGNATURES)
 
 Limit the objective value to `tolerance`-times the current objective value, as
 with [`objective_bounds`](@ref).
@@ -13,7 +13,7 @@ constrain_objective_value(tolerance) =
     end
 
 """
-    change_constraint(id::String; lb=nothing, ub=nothing)
+$(TYPEDSIGNATURES)
 
 Change the lower and upper bounds (`lb` and `ub` respectively) of reaction `id` if supplied.
 """
@@ -25,7 +25,7 @@ change_constraint(id::String; lb = nothing, ub = nothing) =
     end
 
 """
-    change_objective(new_objective::Union{String,Vector{String}}; weights=[], sense=MAX_SENSE)
+$(TYPEDSIGNATURES)
 
 Modification that changes the objective function used in a constraint based
 analysis function.  `new_objective` can be a single reaction identifier, or an
diff --git a/src/analysis/modifications/knockout.jl b/src/analysis/modifications/knockout.jl
index a547b281c..b55715e61 100644
--- a/src/analysis/modifications/knockout.jl
+++ b/src/analysis/modifications/knockout.jl
@@ -1,5 +1,5 @@
 """
-    knockout(gene_ids::Vector{String})
+$(TYPEDSIGNATURES)
 
 A modification that zeroes the bounds of all reactions that would be knocked
 out by the combination of specified genes (effectively disabling the
@@ -20,14 +20,14 @@ knockout(gene_ids::Vector{String}) =
     (model, optmodel) -> _do_knockout(model, optmodel, gene_ids)
 
 """
-    knockout(gene_id::String)
+$(TYPEDSIGNATURES)
 
 A helper variant of [`knockout`](@ref) for a single gene.
 """
 knockout(gene_id::String) = knockout([gene_id])
 
 """
-    _do_knockout(model::MetabolicModel, opt_model)
+$(TYPEDSIGNATURES)
 
 Internal helper for knockouts on generic MetabolicModels. This can be
 overloaded so that the knockouts may work differently (more efficiently) with
diff --git a/src/analysis/modifications/loopless.jl b/src/analysis/modifications/loopless.jl
index 32cb70987..b7578ac74 100644
--- a/src/analysis/modifications/loopless.jl
+++ b/src/analysis/modifications/loopless.jl
@@ -1,8 +1,5 @@
 """
-    add_loopless_constraints(;
-        max_flux_bound = _constants.default_reaction_bound,
-        strict_inequality_tolerance = _constants.loopless_strict_inequality_tolerance,
-    )
+$(TYPEDSIGNATURES)
 
 Add quasi-thermodynamic constraints to the model to ensure that no thermodynamically
 infeasible internal cycles can occur. Adds the following constraints to the problem:
diff --git a/src/analysis/modifications/moment.jl b/src/analysis/modifications/moment.jl
index 94616fa3a..31b2e56f6 100644
--- a/src/analysis/modifications/moment.jl
+++ b/src/analysis/modifications/moment.jl
@@ -1,8 +1,5 @@
 """
-    add_moment_constraints(
-        ksas::Dict{String,Float64},
-        protein_mass_fraction::Float64
-    )
+$(TYPEDSIGNATURES)
 
 A modification that adds enzyme capacity constraints to the problem using a _modified_
 version of the MOMENT algorithm. Requires specific activities, `ksas` [mmol product/g
diff --git a/src/analysis/modifications/optimizer.jl b/src/analysis/modifications/optimizer.jl
index 2287ef4be..41c51bdf8 100644
--- a/src/analysis/modifications/optimizer.jl
+++ b/src/analysis/modifications/optimizer.jl
@@ -1,9 +1,9 @@
 
 """
-    change_sense(objective_sense)
+$(TYPEDSIGNATURES)
 
 Change the objective sense of optimization.
-Possible arguments are `MOI.MAX_SENSE` and `MOI.MIN_SENSE`.
+Possible arguments are `MAX_SENSE` and `MIN_SENSE`.
 
 If you want to change the objective and sense at the same time, use
 [`change_objective`](@ref) instead to do both at once.
@@ -12,7 +12,7 @@ change_sense(objective_sense) =
     (_, opt_model) -> set_objective_sense(opt_model, objective_sense)
 
 """
-    change_optimizer(optimizer)
+$(TYPEDSIGNATURES)
 
 Change the JuMP optimizer used to run the optimization.
 
@@ -23,7 +23,7 @@ problems that may require different optimizers for different parts, such as the
 change_optimizer(optimizer) = (_, opt_model) -> set_optimizer(opt_model, optimizer)
 
 """
-    change_optimizer_attribute(attribute_key, value)
+$(TYPEDSIGNATURES)
 
 Change a JuMP optimizer attribute. The attributes are optimizer-specific, refer
 to the JuMP documentation and the documentation of the specific optimizer for
diff --git a/src/analysis/parsimonious_flux_balance_analysis.jl b/src/analysis/parsimonious_flux_balance_analysis.jl
index 12475f15c..8d06f4d45 100644
--- a/src/analysis/parsimonious_flux_balance_analysis.jl
+++ b/src/analysis/parsimonious_flux_balance_analysis.jl
@@ -1,11 +1,5 @@
 """
-    parsimonious_flux_balance_analysis(
-        model::MetabolicModel,
-        optimizer;
-        modifications = [],
-        qp_modifications = [],
-        relax_bounds=[1.0, 0.999999, 0.99999, 0.9999, 0.999, 0.99],
-    )
+$(TYPEDSIGNATURES)
 
 Run parsimonious flux balance analysis (pFBA) on the `model`. In short, pFBA
 runs two consecutive optimization problems. The first is traditional FBA:
@@ -36,14 +30,15 @@ finds a minimal total flux through the model that still satisfies the (slightly
 relaxed) optimum. This is done using a quadratic problem optimizer. If the
 original optimizer does not support quadratic optimization, it can be changed
 using the callback in `qp_modifications`, which are applied after the FBA. See
-the documentation of [`flux_balance_analysis`](@ref) for usage examples of modifications.
+the documentation of [`flux_balance_analysis`](@ref) for usage examples of
+modifications.
 
 Thhe optimum relaxation sequence can be specified in `relax` parameter, it
-defaults to multiplicative range of `[1.0, 0.999999, ..., 0.99]` of the
-original bound.
+defaults to multiplicative range of `[1.0, 0.999999, ..., 0.99]` of the original
+bound.
 
-Returns an optimized model that contains the pFBA solution; or `nothing` if the
-optimization failed.
+Returns an optimized model that contains the pFBA solution (or an unsolved model
+if something went wrong).
 
 # Example
 ```
@@ -61,7 +56,7 @@ function parsimonious_flux_balance_analysis(
 )
     # Run FBA
     opt_model = flux_balance_analysis(model, optimizer; modifications = modifications)
-    is_solved(opt_model) || return nothing # FBA failed
+    is_solved(opt_model) || return opt_model # FBA failed
 
     # get the objective
     Z = objective_value(opt_model)
@@ -88,13 +83,11 @@ function parsimonious_flux_balance_analysis(
         unregister(opt_model, :pfba_constraint)
     end
 
-    is_solved(opt_model) || return nothing # pFBA failed
-
     return opt_model
 end
 
 """
-    parsimonious_flux_balance_analysis_vec(model::MetabolicModel, args...; kwargs...)
+$(TYPEDSIGNATURES)
 
 Perform parsimonious flux balance analysis on `model` using `optimizer`.
 Returns a vector of fluxes in the same order as the reactions in `model`.
@@ -108,7 +101,7 @@ parsimonious_flux_balance_analysis_vec(model::MetabolicModel, args...; kwargs...
     flux_vector(model, parsimonious_flux_balance_analysis(model, args...; kwargs...))
 
 """
-    parsimonious_flux_balance_analysis_dict(model::MetabolicModel, args...; kwargs...)
+$(TYPEDSIGNATURES)
 
 Perform parsimonious flux balance analysis on `model` using `optimizer`.
 Returns a dictionary mapping the reaction IDs to fluxes. Arguments are
diff --git a/src/analysis/sampling/affine_hit_and_run.jl b/src/analysis/sampling/affine_hit_and_run.jl
index 92669cf5b..b369b0be6 100644
--- a/src/analysis/sampling/affine_hit_and_run.jl
+++ b/src/analysis/sampling/affine_hit_and_run.jl
@@ -1,12 +1,5 @@
 """
-    function affine_hit_and_run(
-        m::MetabolicModel,
-        warmup_points::Matrix{Float64};
-        sample_iters = 100 .* (1:5),
-        workers = [myid()],
-        chains = length(workers),
-        seed = rand(Int),
-    )
+$(TYPEDSIGNATURES)
 
 Run a hit-and-run style sampling that starts from `warmup_points` and uses
 their affine combinations for generating the run directions to sample the space
@@ -77,7 +70,7 @@ function affine_hit_and_run(
 end
 
 """
-    _affine_hit_and_run_chain(warmup, lbs, ubs, iters, seed)
+$(TYPEDSIGNATURES)
 
 Internal helper function for computing a single affine hit-and-run chain.
 """
diff --git a/src/analysis/sampling/warmup_variability.jl b/src/analysis/sampling/warmup_variability.jl
index a16078ba7..8764bbeb2 100644
--- a/src/analysis/sampling/warmup_variability.jl
+++ b/src/analysis/sampling/warmup_variability.jl
@@ -1,10 +1,5 @@
 """
-    warmup_from_variability(
-        model::MetabolicModel,
-        optimizer,
-        n_points::Int;
-        kwargs...
-    )
+$(TYPEDSIGNATURES)
 
 Generates FVA-like warmup points for samplers, by selecting random points by
 minimizing and maximizing reactions. Can not return more than 2 times the
@@ -37,14 +32,7 @@ function warmup_from_variability(
 end
 
 """
-    function warmup_from_variability(
-        model::MetabolicModel,
-        optimizer,
-        min_reactions::Vector{Int}=1:n_reactions(model),
-        max_reactions::Vector{Int}=1:n_reactions(model);
-        modifications = [],
-        workers::Vector{Int} = [myid()],
-    )::Matrix{Float64}
+$(TYPEDSIGNATURES)
 
 Generate FVA-like warmup points for samplers, by minimizing and maximizing the
 specified reactions. The result is returned as a matrix, each point occupies as
@@ -99,7 +87,7 @@ function warmup_from_variability(
 end
 
 """
-    _maximize_warmup_reaction(opt_model, rid, ret)
+$(TYPEDSIGNATURES)
 
 A helper function for finding warmup points from reaction variability.
 """
diff --git a/src/analysis/screening.jl b/src/analysis/screening.jl
index 1876fcef6..a4dd13af4 100644
--- a/src/analysis/screening.jl
+++ b/src/analysis/screening.jl
@@ -1,6 +1,6 @@
 
 """
-    _screen_args(argtuple, kwargtuple, modsname)
+$(TYPEDSIGNATURES)
 
 Internal helper to check the presence and shape of modification and argument
 arrays in [`screen`](@ref) and pals.
@@ -33,13 +33,7 @@ function _screen_args(argtuple, kwargtuple, modsname)
 end
 
 """
-    screen(
-        model::MetabolicModel;
-        variants::Array{V,N}, # defaults to an array of identities
-        analysis,
-        args::Array{A,N}, # defaults to an array of empty argument lists
-        workers = [myid()],
-    )::Array where {V<:AbstractVector,A,N}
+$(TYPEDSIGNATURES)
 
 Take an array of model-modifying function vectors in `variants`, and execute
 the function `analysis` on all variants of the `model` specified by `variants`.
@@ -111,13 +105,7 @@ screen(args...; kwargs...) =
     _screen_impl(args...; kwargs..., _screen_args(args, kwargs, :variants)...)
 
 """
-    _screen_impl(
-        model::MetabolicModel;
-        variants::Array{V,N},
-        analysis,
-        args::Array{A,N},
-        workers = [myid()],
-    )::Array where {V<:AbstractVector,A,N}
+$(TYPEDSIGNATURES)
 
 The actual implementation of [`screen`](@ref).
 """
@@ -151,7 +139,7 @@ function _screen_impl(
 end
 
 """
-    screen_variant(model::MetabolicModel, variant::Vector, analysis, args = ())
+$(TYPEDSIGNATURES)
 
 Helper function for [`screen`](@ref) that applies all single-argument
 functions in `variant` to the `model` (in order from "first" to
@@ -167,7 +155,7 @@ function screen_variant(model::MetabolicModel, variant::Vector, analysis, args =
 end
 
 """
-    screen_variants(model, variants, analysis; workers=[myid()])
+$(TYPEDSIGNATURES)
 
 A shortcut for [`screen`](@ref) that only works with model variants.
 """
@@ -175,7 +163,7 @@ screen_variants(model, variants, analysis; workers = [myid()]) =
     screen(model; variants = variants, analysis = analysis, workers = workers)
 
 """
-    screen_optimize_objective(_, optmodel)::Maybe{Float64}
+$(TYPEDSIGNATURES)
 
 A variant of [`optimize_objective`](@ref) directly usable in
 [`screen_optmodel_modifications`](@ref).
@@ -183,7 +171,7 @@ A variant of [`optimize_objective`](@ref) directly usable in
 screen_optimize_objective(_, optmodel)::Maybe{Float64} = optimize_objective(optmodel)
 
 """
-    _screen_optmodel_prepare(model, optimizer, common_modifications)
+$(TYPEDSIGNATURES)
 
 Internal helper for [`screen_optmodel_modifications`](@ref) that creates the
 model and applies the modifications.
@@ -198,7 +186,7 @@ function _screen_optmodel_prepare(model, optimizer, common_modifications)
 end
 
 """
-    _screen_optmodel_item((mods, args))
+$(TYPEDSIGNATURES)
 
 Internal helper for [`screen_optmodel_modifications`](@ref) that computes one
 item of the screening task.
@@ -212,15 +200,7 @@ function _screen_optmodel_item((mods, args))
 end
 
 """
-    screen_optmodel_modifications(
-        model::MetabolicModel,
-        optimizer;
-        common_modifications::VF = [],
-        modifications::Array{V,N}, # defaults to an array with no modifications
-        args::Array{A,N}, # defaults to an array of empty argument lists
-        analysis::Function,
-        workers = [myid()],
-    )::Array where {V<:AbstractVector,VF<:AbstractVector,A,N}
+$(TYPEDSIGNATURES)
 
 Screen multiple modifications of the same optimization model.
 
@@ -254,15 +234,7 @@ screen_optmodel_modifications(args...; kwargs...) = _screen_optmodel_modificatio
 )
 
 """
-    _screen_optmodel_modifications_impl(
-        model::MetabolicModel,
-        optimizer;
-        common_modifications::VF = [],
-        modifications::Array{V,N},
-        args::Array{A,N},
-        analysis::Function,
-        workers = [myid()],
-    )::Array where {V<:AbstractVector,VF<:AbstractVector,A,N}
+$(TYPEDSIGNATURES)
 
 The actual implementation of [`screen_optmodel_modifications`](@ref).
 """
diff --git a/src/analysis/smoment.jl b/src/analysis/smoment.jl
index 391345fb9..179d41597 100644
--- a/src/analysis/smoment.jl
+++ b/src/analysis/smoment.jl
@@ -1,11 +1,6 @@
 
 """
-    make_smoment_model(
-        model::MetabolicModel;
-        reaction_isozyme::Union{Function,Dict{String,Isozyme}},
-        gene_product_molar_mass::Union{Function,Dict{String,Float64}},
-        total_enzyme_capacity::Float64,
-    )
+$(TYPEDSIGNATURES)
 
 Construct a model with a structure given by sMOMENT algorithm; returns a
 [`SMomentModel`](@ref) (see the documentation for details).
diff --git a/src/banner.jl b/src/banner.jl
deleted file mode 100644
index 00f62a550..000000000
--- a/src/banner.jl
+++ /dev/null
@@ -1,32 +0,0 @@
-const _PKG_ROOT_DIR = normpath(joinpath(@__DIR__, ".."))
-include_dependency(joinpath(_PKG_ROOT_DIR, "Project.toml"))
-
-const COBREXA_VERSION =
-    VersionNumber(Pkg.TOML.parsefile(joinpath(_PKG_ROOT_DIR, "Project.toml"))["version"])
-
-function __init__()
-    if myid() == 1 && Base.JLOptions().banner != 0
-        _print_banner()
-    end
-end
-
-function _print_banner()
-    c = Base.text_colors
-    n = c[:normal]
-    y = c[:bold] * c[:cyan]
-
-    println(
-        "
-                   $(y)//$(n)    |
-        \\\\\\\\\\  // $(y)//$(n)     | $(c[:bold])COBREXA.jl $(c[:normal]) v$(COBREXA_VERSION)
-         \\\\ \\\\// $(y)//$(n)      |
-          \\\\ \\/ $(y)//$(n)       | $(c[:bold])CO$(c[:normal])nstraint-$(c[:bold])B$(c[:normal])ased $(c[:bold])R$(c[:normal])econstruction
-           \\\\  $(y)//$(n)        | and $(c[:bold])EX$(c[:normal])ascale $(c[:bold])A$(c[:normal])nalysis in Julia
-           //  $(y)\\\\$(n)        |
-          // $(y)/\\ \\\\$(n)       | See documentation and examples at:
-         // $(y)//\\\\ \\\\$(n)      | https://lcsb-biocore.github.io/COBREXA.jl
-        // $(y)//  \\\\\\\\\\$(n)     |
-       //                |
-        ",
-    )
-end
diff --git a/src/base/constants.jl b/src/base/constants.jl
index 3a752c11c..001c82087 100644
--- a/src/base/constants.jl
+++ b/src/base/constants.jl
@@ -63,6 +63,3 @@ const _constants = (
     T = 298.15, # Kelvin
     R = 8.31446261815324e-3, # kJ/K/mol
 )
-
-const MAX_SENSE = MOI.MAX_SENSE
-const MIN_SENSE = MOI.MIN_SENSE
diff --git a/src/base/identifiers.jl b/src/base/identifiers.jl
new file mode 100644
index 000000000..92b147a93
--- /dev/null
+++ b/src/base/identifiers.jl
@@ -0,0 +1,33 @@
+"""
+This module uses annotation identifiers to classify reactions, metabolites,
+genes, etc. If an subject has a matching annotation, then it is assumed that it
+is part of the associated class of objects.
+"""
+module Identifiers
+using ..COBREXA.SBOTerms
+
+const EXCHANGE_REACTIONS = [SBOTerms.EXCHANGE_REACTION]
+
+const TRANSPORT_REACTIONS = [
+    SBOTerms.TRANSPORT_REACTION,
+    SBOTerms.TRANSCELLULAR_MEMBRANE_INFLUX_REACTION,
+    SBOTerms.TRANSCELLULAR_MEMBRANE_EFFLUX_REACTION,
+]
+
+const METABOLIC_REACTIONS = [SBOTerms.BIOCHEMICAL_REACTION]
+
+const BIOMASS_REACTIONS = [SBOTerms.BIOMASS_PRODUCTION]
+
+const ATP_MAINTENANCE_REACTIONS = [SBOTerms.ATP_MAINTENANCE]
+
+const PSEUDOREACTIONS = [
+    SBOTerms.EXCHANGE_REACTION,
+    SBOTerms.DEMAND_REACTION,
+    SBOTerms.BIOMASS_PRODUCTION,
+    SBOTerms.ATP_MAINTENANCE,
+    SBOTerms.PSEUDOREACTION,
+    SBOTerms.SINK_REACTION,
+]
+
+const SPONTANEOUS_REACTIONS = [SBOTerms.SPONTANEOUS_REACTION]
+end
diff --git a/src/base/logging/log.jl b/src/base/logging/log.jl
index 7b24ba874..866846988 100644
--- a/src/base/logging/log.jl
+++ b/src/base/logging/log.jl
@@ -1,6 +1,6 @@
 
 """
-    macro _make_logging_group(sym::Symbol, doc::String)
+$(TYPEDSIGNATURES)
 
 This creates a group of functions that allow masking out topic-related logging
 actions. A call that goes as follows:
diff --git a/src/base/macros/is_xxx_reaction.jl b/src/base/macros/is_xxx_reaction.jl
new file mode 100644
index 000000000..b8601df86
--- /dev/null
+++ b/src/base/macros/is_xxx_reaction.jl
@@ -0,0 +1,49 @@
+
+"""
+@_is_reaction_fn(anno_id, identifier)
+
+A helper for creating functions like `is_exchange_reaction`.
+"""
+macro _is_reaction_fn(anno_id, identifiers)
+
+    fname = Symbol(:is_, anno_id, :_reaction)
+    grammar = any(startswith.(anno_id, ["a", "e", "i", "o", "u"])) ? "an" : "a"
+
+    body = quote
+        begin
+            anno = reaction_annotations(model, reaction_id)
+            for key in annotation_keys
+                if haskey(anno, key)
+                    any(in.($identifiers, Ref(anno[key]))) && return true
+                end
+            end
+            return false
+        end
+    end
+
+    docstring = """
+        $fname(
+            model::MetabolicModel,
+            reaction_id::String;
+            annotation_keys = ["sbo", "SBO"],
+        )
+
+    Check if a reaction is annotated as $(grammar) $(anno_id) reaction. Uses
+    `$identifiers` internally, which includes SBO identifiers. In
+    the reaction annotations, use the keys in `annotation_keys` to look for entries.
+    Returns false if no hits or if no keys are found.
+    """
+    Expr(
+        :macrocall,
+        Symbol("@doc"),
+        __source__,
+        docstring,
+        :(
+            $fname(
+                model::MetabolicModel,
+                reaction_id::String;
+                annotation_keys = ["sbo", "SBO"],
+            ) = $body
+        ),
+    )
+end
diff --git a/src/base/macros/model_wrapper.jl b/src/base/macros/model_wrapper.jl
index ccbdb0e29..66de84db6 100644
--- a/src/base/macros/model_wrapper.jl
+++ b/src/base/macros/model_wrapper.jl
@@ -1,6 +1,6 @@
 
 """
-    _inherit_model_methods_impl(mtype::Symbol, arglist, access, fwdlist, fns...)
+$(TYPEDSIGNATURES)
 
 A helper backend for [`@_inherit_model_methods`](@ref) and
 [`@_inherit_model_methods_fn`](@ref).
@@ -38,7 +38,7 @@ function _inherit_model_methods_impl(
 end
 
 """
-    @_inherit_model_methods
+$(TYPEDSIGNATURES)
 
 Generates trivial accessor functions listed in `fns` for a model that is
 wrapped in type `mtype` as field `member`.
@@ -55,7 +55,7 @@ macro _inherit_model_methods(mtype::Symbol, arglist, member::Symbol, fwdlist, fn
 end
 
 """
-    @_inherit_model_methods_fn
+$(TYPEDSIGNATURES)
 
 A more generic version of [`@_inherit_model_methods`](@ref) that accesses the
 "inner" model using an accessor function name.
diff --git a/src/base/ontologies/SBOTerms.jl b/src/base/ontologies/SBOTerms.jl
new file mode 100644
index 000000000..6a3e2a4f3
--- /dev/null
+++ b/src/base/ontologies/SBOTerms.jl
@@ -0,0 +1,84 @@
+"""
+This module contains SBO terms recognized by COBREXA. For the full ontology, see
+https://github.com/EBI-BioModels/SBO/blob/master/SBO_OBO.obo.
+
+If an SBO term appears here it *may* be used in a function; if an SBO term does
+not appear here, then it is *not* used in a COBREXA function.
+
+These terms are used in `Identifiers.jl` which groups them as appropriate for
+use in functions that classify reactions, metabolites, etc.
+"""
+module SBOTerms
+const FLUX_BALANCE_FRAMEWORK = "SBO:0000624"
+const RESOURCE_BALANCE_FRAMEWORK = "SBO:0000692"
+const CONSTRAINT_BASED_FRAMEWORK = "SBO:0000693"
+
+const PRODUCT = "SBO:0000011"
+const CONCENTRATION_OF_PRODUCT = "SBO:0000512"
+const SIDE_PRODUCT = "SBO:0000603"
+
+const SUBSTRATE = "SBO:0000015"
+const CONCENTRATION_OF_SUBSTRATE = "SBO:0000515"
+const SIDE_SUBSTRATE = "SBO:0000604"
+
+const ENZYME = "SBO:0000014"
+const TOTAL_CONCENTRATION_OF_ENZYME = "SBO:0000300"
+
+const TRANSCRIPTION = "SBO:0000183"
+const TRANSLATION = "SBO:0000184"
+
+const GENE = "SBO:0000243"
+const METABOLITE = "SBO:0000299"
+const MACROMOLECULE = "SBO:0000245"
+const SIMPLE_CHEMICAL = "SBO:0000247"
+const RIBONUCLEIC_ACID = "SBO:0000250"
+const DEOXYRIBONUCLEIC_ACID = "SBO:0000251"
+const TRANSFER_RNA = "SBO:0000313"
+const RIBOSOMAL_RNA = "SBO:0000314"
+const MESSENGER_RNA = "SBO:0000278"
+const TRANSPORTER = "SBO:0000284"
+const PROTEIN_COMPLEX = "SBO:0000297"
+
+const MOLECULAR_MASS = "SBO:0000647"
+const CATALYTIC_RATE_CONSTANT = "SBO:0000025" # turnover number synonym
+const CAPACITY = "SBO:0000661"
+const MICHAELIS_CONSTANT = "SBO:0000027"
+const MICHAELIS_CONSTANT_FOR_PRODUCT = "SBO:0000323"
+const MICHAELIS_CONSTANT_FOR_SUBSTRATE = "SBO:0000322"
+const INHIBITORY_CONSTANT = "SBO:0000261"
+
+const STOICHIOMETRIC_COEFFICIENT = "SBO:0000481"
+const AND = "SBO:0000173"
+const OR = "SBO:0000174"
+
+const PH = "SBO:0000304"
+const IONIC_STRENGTH = "SBO:0000623"
+
+const THERMODYNAMIC_TEMPERATURE = "SBO:0000147"
+const STANDARD_GIBBS_FREE_ENERGY_OF_REACTION = "SBO:0000583"
+const GIBBS_FREE_ENERGY_OF_REACTION = "SBO:0000617"
+const STANDARD_GIBBS_FREE_ENERGY_OF_FORMATION = "SBO:0000582"
+const TRANSFORMED_STANDARD_GIBBS_FREE_ENERGY_CHANGE_OF_REACTION = "SBO:0000620"
+const TRANSFORMED_GIBBS_FREE_ENERGY_CHANGE_OF_REACTION = "SBO:0000622"
+const TRANSFORMED_STANDARD_GIBBS_FREE_ENERGY_OF_FORMATION = "SBO:0000621"
+
+const BIOCHEMICAL_OR_TRANSPORT_REACTION = "SBO:0000167"
+const BIOCHEMICAL_REACTION = "SBO:0000176"
+const TRANSPORT_REACTION = "SBO:0000655"
+const TRANSCELLULAR_MEMBRANE_INFLUX_REACTION = "SBO:0000587"
+const TRANSCELLULAR_MEMBRANE_EFFLUX_REACTION = "SBO:0000588"
+const FLUX_BOUND = "SBO:0000625"
+const DEFAULT_FLUX_BOUND = "SBO:0000626"
+const EXCHANGE_REACTION = "SBO:0000627"
+const DEMAND_REACTION = "SBO:0000628"
+const BIOMASS_PRODUCTION = "SBO:0000629"
+const ATP_MAINTENANCE = "SBO:0000630"
+const PSEUDOREACTION = "SBO:0000631"
+const SINK_REACTION = "SBO:0000632"
+const SPONTANEOUS_REACTION = "SBO:0000672"
+
+const ACTIVE_TRANSPORT = "SBO:0000657"
+const PASSIVE_TRANSPORT = "SBO:0000658"
+
+const SUBSYSTEM = "SBO:0000633"
+end
diff --git a/src/base/solver.jl b/src/base/solver.jl
index 02735a2bc..7d112e7d8 100644
--- a/src/base/solver.jl
+++ b/src/base/solver.jl
@@ -1,10 +1,6 @@
 
 """
-    make_optimization_model(
-        model::MetabolicModel,
-        optimizer;
-        sense = MAX_SENSE,
-    )
+$(TYPEDSIGNATURES)
 
 Convert `MetabolicModel`s to a JuMP model, place objectives and the equality
 constraint.
@@ -36,7 +32,7 @@ function make_optimization_model(model::MetabolicModel, optimizer; sense = MAX_S
 end
 
 """
-    is_solved(opt_model)
+$(TYPEDSIGNATURES)
 
 Return `true` if `opt_model` solved successfully (solution is optimal or locally
 optimal).  Return `false` if any other termination status is reached.
@@ -45,7 +41,7 @@ Termination status is defined in the documentation of `JuMP`.
 is_solved(opt_model) = termination_status(opt_model) in [MOI.OPTIMAL, MOI.LOCALLY_SOLVED]
 
 """
-    optimize_objective(opt_model)::Maybe{Float64}
+$(TYPEDSIGNATURES)
 
 Shortcut for running JuMP `optimize!` on a model and returning the objective
 value, if solved.
@@ -56,7 +52,7 @@ function optimize_objective(opt_model)::Maybe{Float64}
 end
 
 """
-    get_optmodel_bounds(opt_model)
+$(TYPEDSIGNATURES)
 
 Returns vectors of the lower and upper bounds of `opt_model` constraints, where
 `opt_model` is a JuMP model constructed by e.g.
@@ -68,10 +64,7 @@ get_optmodel_bounds(opt_model) = (
 )
 
 """
-    set_optmodel_bound!(vidx, opt_model;
-        ub::Maybe{Real} = nothing,
-        lb::Maybe{Real} = nothing,
-    )
+$(TYPEDSIGNATURES)
 
 Helper function to set the bounds of a variable in the model. Internally calls
 `set_normalized_rhs` from JuMP. If the bounds are set to `nothing`, they will
@@ -88,7 +81,7 @@ function set_optmodel_bound!(
 end
 
 """
-    solved_objective_value(opt_model)::Maybe{Float64}
+$(TYPEDSIGNATURES)
 
 Returns the current objective value of a model, if solved.
 
@@ -101,7 +94,7 @@ solved_objective_value(opt_model)::Maybe{Float64} =
     is_solved(opt_model) ? objective_value(opt_model) : nothing
 
 """
-    flux_vector(opt_model)::Maybe{Vector{Float64}}
+$(TYPEDSIGNATURES)
 
 Returns a vector of fluxes of the model, if solved.
 
@@ -114,7 +107,7 @@ flux_vector(model::MetabolicModel, opt_model)::Maybe{Vector{Float64}} =
     is_solved(opt_model) ? reaction_flux(model)' * value.(opt_model[:x]) : nothing
 
 """
-    flux_dict(model::MetabolicModel, opt_model)::Maybe{Dict{String, Float64}, Nothing}
+$(TYPEDSIGNATURES)
 
 Returns the fluxes of the model as a reaction-keyed dictionary, if solved.
 
@@ -128,7 +121,7 @@ flux_dict(model::MetabolicModel, opt_model)::Maybe{Dict{String,Float64}} =
     Dict(fluxes(model) .=> reaction_flux(model)' * value.(opt_model[:x])) : nothing
 
 """
-    flux_dict(model::MetabolicModel)
+$(TYPEDSIGNATURES)
 
 A pipeable variant of `flux_dict`.
 
diff --git a/src/base/types/CoreModel.jl b/src/base/types/CoreModel.jl
index db696c42a..9133fda6a 100644
--- a/src/base/types/CoreModel.jl
+++ b/src/base/types/CoreModel.jl
@@ -1,6 +1,6 @@
 
 """
-    struct CoreModel <: MetabolicModel
+$(TYPEDEF)
 
 A "bare bones" core linear optimization problem of the form, with reaction and
 metabolite names.
@@ -9,6 +9,9 @@ min c^T x
 s.t. S x = b
       xₗ ≤ x ≤ xᵤ
 ```
+
+# Fields
+$(TYPEDFIELDS)
 """
 mutable struct CoreModel <: MetabolicModel
     S::SparseMat
@@ -45,49 +48,49 @@ mutable struct CoreModel <: MetabolicModel
 end
 
 """
-    reactions(a::CoreModel)::Vector{String}
+$(TYPEDSIGNATURES)
 
 Get the reactions in a `CoreModel`.
 """
 reactions(a::CoreModel)::Vector{String} = a.rxns
 
 """
-    metabolites(a::CoreModel)::Vector{String}
+$(TYPEDSIGNATURES)
 
 Metabolites in a `CoreModel`.
 """
 metabolites(a::CoreModel)::Vector{String} = a.mets
 
 """
-    stoichiometry(a::CoreModel)::SparseMat
+$(TYPEDSIGNATURES)
 
 `CoreModel` stoichiometry matrix.
 """
 stoichiometry(a::CoreModel)::SparseMat = a.S
 
 """
-    bounds(a::CoreModel)::Tuple{Vector{Float64},Vector{Float64}}
+$(TYPEDSIGNATURES)
 
 `CoreModel` flux bounds.
 """
 bounds(a::CoreModel)::Tuple{Vector{Float64},Vector{Float64}} = (a.xl, a.xu)
 
 """
-    balance(a::CoreModel)::SparseVec
+$(TYPEDSIGNATURES)
 
 `CoreModel` target flux balance.
 """
 balance(a::CoreModel)::SparseVec = a.b
 
 """
-    objective(a::CoreModel)::SparseVec
+$(TYPEDSIGNATURES)
 
 `CoreModel` objective vector.
 """
 objective(a::CoreModel)::SparseVec = a.c
 
 """
-    genes(a::CoreModel)::Vector{String}
+$(TYPEDSIGNATURES)
 
 Collect all genes contained in the [`CoreModel`](@ref). The call is expensive
 for large models, because the vector is not stored and instead gets rebuilt
@@ -107,7 +110,7 @@ function genes(a::CoreModel)::Vector{String}
 end
 
 """
-    reaction_stoichiometry(model::CoreModel, rid::String)::Dict{String, Float64}
+$(TYPEDSIGNATURES)
 
 Return the stoichiometry of reaction with ID `rid`.
 """
@@ -115,7 +118,7 @@ reaction_stoichiometry(m::CoreModel, rid::String)::Dict{String,Float64} =
     Dict(m.mets[k] => v for (k, v) in zip(findnz(m.S[:, first(indexin([rid], m.rxns))])...))
 
 """
-    reaction_stoichiometry(model::CoreModel, ridx)::Dict{String, Float64}
+$(TYPEDSIGNATURES)
 
 Return the stoichiometry of reaction at index `ridx`.
 """
@@ -123,7 +126,7 @@ reaction_stoichiometry(m::CoreModel, ridx)::Dict{String,Float64} =
     Dict(m.mets[k] => v for (k, v) in zip(findnz(m.S[:, ridx])...))
 
 """
-    reaction_gene_association_vec(model::CoreModel)::Vector{Maybe{GeneAssociation}}
+$(TYPEDSIGNATURES)
 
 Retrieve a vector of all gene associations in a [`CoreModel`](@ref), in the
 same order as `reactions(model)`.
@@ -131,7 +134,7 @@ same order as `reactions(model)`.
 reaction_gene_association_vec(model::CoreModel)::Vector{Maybe{GeneAssociation}} = model.grrs
 
 """
-    reaction_gene_association(model::CoreModel, ridx::Int)::Maybe{GeneAssociation}
+$(TYPEDSIGNATURES)
 
 Retrieve the [`GeneAssociation`](@ref) from [`CoreModel`](@ref) by reaction
 index.
@@ -140,7 +143,7 @@ reaction_gene_association(model::CoreModel, ridx::Int)::Maybe{GeneAssociation} =
     model.grrs[ridx]
 
 """
-    reaction_gene_association(model::CoreModel, rid::String)::Maybe{GeneAssociation}
+$(TYPEDSIGNATURES)
 
 Retrieve the [`GeneAssociation`](@ref) from [`CoreModel`](@ref) by reaction ID.
 """
@@ -148,7 +151,7 @@ reaction_gene_association(model::CoreModel, rid::String)::Maybe{GeneAssociation}
     model.grrs[first(indexin([rid], model.rxns))]
 
 """
-    Base.convert(::Type{CoreModel}, m::M) where {M <: MetabolicModel}
+$(TYPEDSIGNATURES)
 
 Make a `CoreModel` out of any compatible model type.
 """
diff --git a/src/base/types/CoreModelCoupled.jl b/src/base/types/CoreModelCoupled.jl
index fd1fb4735..048ffa730 100644
--- a/src/base/types/CoreModelCoupled.jl
+++ b/src/base/types/CoreModelCoupled.jl
@@ -1,12 +1,15 @@
 
 """
-    mutable struct CoreCoupling{M} <: ModelWrapper where {M<:MetabolicModel}
+$(TYPEDEF)
 
 A matrix-based wrap that adds reaction coupling matrix to the inner model. A
 flux `x` feasible in this model must satisfy:
 ```
     cₗ ≤ C x ≤ cᵤ
 ```
+
+# Fields
+$(TYPEDFIELDS)
 """
 mutable struct CoreCoupling{M} <: ModelWrapper where {M<:MetabolicModel}
     lm::M
@@ -30,28 +33,28 @@ mutable struct CoreCoupling{M} <: ModelWrapper where {M<:MetabolicModel}
 end
 
 """
-    unwrap_model(a::CoreCoupling)
+$(TYPEDSIGNATURES)
 
 Get the internal [`CoreModel`](@ref) out of [`CoreCoupling`](@ref).
 """
 unwrap_model(a::CoreCoupling) = a.lm
 
 """
-    coupling(a::CoreCoupling)::SparseMat
+$(TYPEDSIGNATURES)
 
 Coupling constraint matrix for a `CoreCoupling`.
 """
 coupling(a::CoreCoupling)::SparseMat = vcat(coupling(a.lm), a.C)
 
 """
-    n_coupling_constraints(a::CoreCoupling)::Int
+$(TYPEDSIGNATURES)
 
 The number of coupling constraints in a `CoreCoupling`.
 """
 n_coupling_constraints(a::CoreCoupling)::Int = n_coupling_constraints(a.lm) + size(a.C, 1)
 
 """
-    coupling_bounds(a::CoreCoupling)::Tuple{Vector{Float64},Vector{Float64}}
+$(TYPEDSIGNATURES)
 
 Coupling bounds for a `CoreCoupling`.
 """
@@ -59,7 +62,7 @@ coupling_bounds(a::CoreCoupling)::Tuple{Vector{Float64},Vector{Float64}} =
     vcat.(coupling_bounds(a.lm), (a.cl, a.cu))
 
 """
-    Base.convert(::Type{CoreCoupling{M}}, mm::MetabolicModel; clone_coupling = true) where {M}
+$(TYPEDSIGNATURES)
 
 Make a `CoreCoupling` out of any compatible model type.
 """
diff --git a/src/base/types/FluxSummary.jl b/src/base/types/FluxSummary.jl
index c1b65fc7e..13f136225 100644
--- a/src/base/types/FluxSummary.jl
+++ b/src/base/types/FluxSummary.jl
@@ -1,8 +1,11 @@
 """
-    FluxSummary
+$(TYPEDEF)
 
 A struct used to store summary information about the solution
 of a constraint based analysis result.
+
+# Fields
+$(TYPEDFIELDS)
 """
 struct FluxSummary
     biomass_fluxes::OrderedDict{String,Float64}
@@ -12,7 +15,7 @@ struct FluxSummary
 end
 
 """
-    FluxSummary()
+$(TYPEDSIGNATURES)
 
 A default empty constructor for `FluxSummary`.
 """
@@ -26,15 +29,7 @@ function FluxSummary()
 end
 
 """
-    flux_summary(flux_result::Dict{String, Float64};
-        exclude_exchanges = false,
-        exchange_prefixes = _constants.exchange_prefixes,
-        biomass_strings = _constants.biomass_strings,
-        exclude_biomass = false,
-        small_flux_bound = 1.0/_constants.default_reaction_bound^2,
-        large_flux_bound = _constants.default_reaction_bound,
-        keep_unbounded = false,
-    )::FluxSummary
+$(TYPEDSIGNATURES)
 
 Summarize a dictionary of fluxes into small, useful representation of the most
 important information contained. Useful for pretty-printing and quickly
diff --git a/src/base/types/FluxVariabilitySummary.jl b/src/base/types/FluxVariabilitySummary.jl
index 97a5edc94..c1b451096 100644
--- a/src/base/types/FluxVariabilitySummary.jl
+++ b/src/base/types/FluxVariabilitySummary.jl
@@ -1,7 +1,10 @@
 """
-    FluxVariabilitySummary
+$(TYPEDEF)
 
 Stores summary information about the result of a flux variability analysis.
+
+# Fields
+$(TYPEDFIELDS)
 """
 struct FluxVariabilitySummary
     biomass_fluxes::Dict{String,Vector{Maybe{Float64}}}
@@ -9,7 +12,7 @@ struct FluxVariabilitySummary
 end
 
 """
-    FluxVariabilitySummary()
+$(TYPEDSIGNATURES)
 
 A default empty constructor for [`FluxVariabilitySummary`](@ref).
 """
@@ -21,12 +24,7 @@ function FluxVariabilitySummary()
 end
 
 """
-    flux_variability_summary(flux_result::Tuple{Dict{String, Dict{String, Float64}}, Dict{String, Dict{String, Float64}}};
-        exclude_exchanges = false,
-        exchange_prefixes = _constants.exchange_prefixes,
-        biomass_strings = _constants.biomass_strings,
-        exclude_biomass = false,
-        )::FluxVariabilitySummary
+$(TYPEDSIGNATURES)
 
 Summarize a dictionary of flux dictionaries obtained eg. from
 [`flux_variability_analysis_dict`](@ref). The simplified summary representation
diff --git a/src/base/types/Gene.jl b/src/base/types/Gene.jl
index b27dec9fa..909c60411 100644
--- a/src/base/types/Gene.jl
+++ b/src/base/types/Gene.jl
@@ -1,20 +1,20 @@
 """
-Gene struct.
+$(TYPEDEF)
 
 # Fields
-````
-id :: String
-name :: Maybe{String}
-notes :: Dict{String, Vector{String}}
-annotation :: Dict{String, Union{Vector{String}, String}}
-````
+$(TYPEDFIELDS)
 """
 mutable struct Gene
     id::String
     name::Maybe{String}
     notes::Notes
     annotations::Annotations
-
-    Gene(id::String = ""; name = nothing, notes = Notes(), annotations = Annotations()) =
-        new(id, name, notes, annotations)
 end
+
+"""
+$(TYPEDSIGNATURES)
+
+A convenient constructor for a `Gene`.
+"""
+Gene(id = ""; name = nothing, notes = Notes(), annotations = Annotations()) =
+    Gene(String(id), name, notes, annotations)
diff --git a/src/base/types/HDF5Model.jl b/src/base/types/HDF5Model.jl
index 38907b0ff..c6b710733 100644
--- a/src/base/types/HDF5Model.jl
+++ b/src/base/types/HDF5Model.jl
@@ -1,6 +1,6 @@
 
 """
-    struct HDF5Model
+$(TYPEDEF)
 
 A model that is stored in HDF5 format. The model data is never really pulled
 into memory, but instead mmap'ed as directly as possible into the Julia
@@ -15,6 +15,9 @@ create a temporary model that behaves like a model "in memory", save it to a
 temporary file. For related reasons, you can not use `convert` models to
 `HDF5Model` format, because the conversion would impliy having the model saved
 somewhere.
+
+# Fields
+$(TYPEDFIELDS)
 """
 mutable struct HDF5Model <: MetabolicModel
     h5::Maybe{HDF5.File}
diff --git a/src/base/types/Isozyme.jl b/src/base/types/Isozyme.jl
index 685f86c35..b7dc6cb9b 100644
--- a/src/base/types/Isozyme.jl
+++ b/src/base/types/Isozyme.jl
@@ -1,15 +1,10 @@
 """
-    mutable struct Isozyme
+$(TYPEDEF)
 
 Information about isozyme composition and activity.
 
 # Fields
-- `gene_product_count :: Dict{String, Int}` assigns each gene product ID its
-  count in the isozyme complex (which is used to determine the total mass of
-  the isozyme)
-- `kcat_forward`, `kcat_reverse` -- forward and reverse turnover numbers of the
-  isozyme
-````
+$(TYPEDFIELDS)
 """
 mutable struct Isozyme
     gene_product_count::Dict{String,Int}
diff --git a/src/base/types/JSONModel.jl b/src/base/types/JSONModel.jl
index 535afc1cd..03ab30a5d 100644
--- a/src/base/types/JSONModel.jl
+++ b/src/base/types/JSONModel.jl
@@ -1,13 +1,5 @@
 """
-    struct JSONModel <: MetabolicModel
-        json::Dict{String,Any}
-        rxn_index::Dict{String,Int}
-        rxns::Vector{Any}
-        met_index::Dict{String,Int}
-        mets::Vector{Any}
-        gene_index::Dict{String,Int}
-        genes::Vector{Any}
-    end
+$(TYPEDEF)
 
 A struct used to store the contents of a JSON model, i.e. a model read from a
 file ending with `.json`. These model files typically store all the model data
@@ -28,6 +20,9 @@ model = load_json_model("some_model.json")
 model.json # see the actual underlying JSON
 reactions(model) # see the list of reactions
 ````
+
+# Fields
+$(TYPEDFIELDS)
 """
 struct JSONModel <: MetabolicModel
     json::Dict{String,Any}
@@ -77,28 +72,28 @@ end
 _parse_notes(x)::Notes = _parse_annotations(x)
 
 """
-    reactions(model::JSONModel)
+$(TYPEDSIGNATURES)
 
 Extract reaction names (stored as `.id`) from JSON model.
 """
 reactions(model::JSONModel) = [_json_rxn_name(r, i) for (i, r) in enumerate(model.rxns)]
 
 """
-    metabolites(model::JSONModel)
+$(TYPEDSIGNATURES)
 
 Extract metabolite names (stored as `.id`) from JSON model.
 """
 metabolites(model::JSONModel) = [_json_met_name(m, i) for (i, m) in enumerate(model.mets)]
 
 """
-    genes(model::JSONModel)
+$(TYPEDSIGNATURES)
 
 Extract gene names from a JSON model.
 """
 genes(model::JSONModel) = [_json_gene_name(g, i) for (i, g) in enumerate(model.genes)]
 
 """
-    stoichiometry(model::JSONModel)
+$(TYPEDSIGNATURES)
 
 Get the stoichiometry. Assuming the information is stored in reaction object
 under key `.metabolites`.
@@ -140,7 +135,7 @@ function stoichiometry(model::JSONModel)
 end
 
 """
-    bounds(model::JSONModel)
+$(TYPEDSIGNATURES)
 
 Get the bounds for reactions, assuming the information is stored in
 `.lower_bound` and `.upper_bound`.
@@ -151,7 +146,7 @@ bounds(model::JSONModel) = (
 )
 
 """
-    objective(model::JSONModel)
+$(TYPEDSIGNATURES)
 
 Collect `.objective_coefficient` keys from model reactions.
 """
@@ -159,7 +154,7 @@ objective(model::JSONModel) =
     sparse([float(get(rxn, "objective_coefficient", 0.0)) for rxn in model.rxns])
 
 """
-    reaction_gene_associaton(model::JSONModel, rid::String)
+$(TYPEDSIGNATURES)
 
 Parses the `.gene_reaction_rule` from reactions.
 """
@@ -169,7 +164,7 @@ reaction_gene_association(model::JSONModel, rid::String) = _maybemap(
 )
 
 """
-    reaction_subsystem(model::JSONModel, rid::String)
+$(TYPEDSIGNATURES)
 
 Parses the `.subsystem` out from reactions.
 """
@@ -177,7 +172,7 @@ reaction_subsystem(model::JSONModel, rid::String) =
     get(model.rxns[model.rxn_index[rid]], "subsystem", nothing)
 
 """
-    metabolite_formula(model::JSONModel, mid::String)
+$(TYPEDSIGNATURES)
 
 Parse and return the metabolite `.formula`
 """
@@ -185,7 +180,7 @@ metabolite_formula(model::JSONModel, mid::String) =
     _maybemap(_parse_formula, get(model.mets[model.met_index[mid]], "formula", nothing))
 
 """
-    metabolite_charge(model::JSONModel, mid::String)
+$(TYPEDSIGNATURES)
 
 Return the metabolite `.charge`
 """
@@ -193,7 +188,7 @@ metabolite_charge(model::JSONModel, mid::String) =
     get(model.mets[model.met_index[mid]], "charge", 0)
 
 """
-    metabolite_compartment(model::JSONModel, mid::String)
+$(TYPEDSIGNATURES)
 
 Return the metabolite `.compartment`
 """
@@ -201,7 +196,7 @@ metabolite_compartment(model::JSONModel, mid::String) =
     get(model.mets[model.met_index[mid]], "compartment", nothing)
 
 """
-    gene_annotations(model::JSONModel, gid::String)::Annotations
+$(TYPEDSIGNATURES)
 
 Gene annotations from the [`JSONModel`](@ref).
 """
@@ -211,7 +206,7 @@ gene_annotations(model::JSONModel, gid::String)::Annotations = _maybemap(
 )
 
 """
-    gene_notes(model::JSONModel, gid::String)::Notes
+$(TYPEDSIGNATURES)
 
 Gene notes from the [`JSONModel`](@ref).
 """
@@ -219,7 +214,7 @@ gene_notes(model::JSONModel, gid::String)::Notes =
     _maybemap(_parse_notes, get(model.genes[model.gene_index[gid]], "notes", nothing))
 
 """
-    reaction_annotations(model::JSONModel, rid::String)::Annotations
+$(TYPEDSIGNATURES)
 
 Reaction annotations from the [`JSONModel`](@ref).
 """
@@ -229,7 +224,7 @@ reaction_annotations(model::JSONModel, rid::String)::Annotations = _maybemap(
 )
 
 """
-    reaction_notes(model::JSONModel, rid::String)::Notes
+$(TYPEDSIGNATURES)
 
 Reaction notes from the [`JSONModel`](@ref).
 """
@@ -237,7 +232,7 @@ reaction_notes(model::JSONModel, rid::String)::Notes =
     _maybemap(_parse_notes, get(model.rxns[model.rxn_index[rid]], "notes", nothing))
 
 """
-    metabolite_annotations(model::JSONModel, mid::String)::Annotations
+$(TYPEDSIGNATURES)
 
 Metabolite annotations from the [`JSONModel`](@ref).
 """
@@ -247,7 +242,7 @@ metabolite_annotations(model::JSONModel, mid::String)::Annotations = _maybemap(
 )
 
 """
-    metabolite_notes(model::JSONModel, mid::String)::Notes
+$(TYPEDSIGNATURES)
 
 Metabolite notes from the [`JSONModel`](@ref).
 """
@@ -255,7 +250,7 @@ metabolite_notes(model::JSONModel, mid::String)::Notes =
     _maybemap(_parse_notes, get(model.mets[model.met_index[mid]], "notes", nothing))
 
 """
-    reaction_stoichiometry(model::JSONModel, rid::String)::Dict{String, Float64}
+$(TYPEDSIGNATURES)
 
 Return the stoichiometry of reaction with ID `rid`.
 """
@@ -263,7 +258,7 @@ reaction_stoichiometry(model::JSONModel, rid::String)::Dict{String,Float64} =
     model.rxns[model.rxn_index[rid]]["metabolites"]
 
 """
-    reaction_name(model::JSONModel, rid::String)
+$(TYPEDSIGNATURES)
 
 Return the name of reaction with ID `rid`.
 """
@@ -271,7 +266,7 @@ reaction_name(model::JSONModel, rid::String) =
     get(model.rxns[model.rxn_index[rid]], "name", nothing)
 
 """
-    metabolite_name(model::JSONModel, mid::String)
+$(TYPEDSIGNATURES)
 
 Return the name of metabolite with ID `mid`.
 """
@@ -279,7 +274,7 @@ metabolite_name(model::JSONModel, mid::String) =
     get(model.mets[model.met_index[mid]], "name", nothing)
 
 """
-    gene_name(model::JSONModel, gid::String)
+$(TYPEDSIGNATURES)
 
 Return the name of gene with ID `gid`.
 """
@@ -287,7 +282,7 @@ gene_name(model::JSONModel, gid::String) =
     get(model.genes[model.gene_index[gid]], "name", nothing)
 
 """
-    Base.convert(::Type{JSONModel}, mm::MetabolicModel)
+$(TYPEDSIGNATURES)
 
 Convert any [`MetabolicModel`](@ref) to [`JSONModel`](@ref).
 """
diff --git a/src/base/types/MATModel.jl b/src/base/types/MATModel.jl
index aa0fb7ff3..bd8d6f512 100644
--- a/src/base/types/MATModel.jl
+++ b/src/base/types/MATModel.jl
@@ -1,7 +1,10 @@
 """
-    struct MATModel
+$(TYPEDEF)
 
 Wrapper around the models loaded in dictionaries from the MATLAB representation.
+
+# Fields
+$(TYPEDFIELDS)
 """
 struct MATModel <: MetabolicModel
     mat::Dict{String,Any}
@@ -11,7 +14,7 @@ n_metabolites(m::MATModel)::Int = size(m.mat["S"], 1)
 n_reactions(m::MATModel)::Int = size(m.mat["S"], 2)
 
 """
-    reactions(m::MATModel)::Vector{String}
+$(TYPEDSIGNATURES)
 
 Extracts reaction names from `rxns` key in the MAT file.
 """
@@ -24,7 +27,7 @@ function reactions(m::MATModel)::Vector{String}
 end
 
 """
-    _mat_has_squashed_coupling(mat)
+$(TYPEDSIGNATURES)
 
 Guesses whether C in the MAT file is stored in A=[S;C].
 """
@@ -33,7 +36,7 @@ _mat_has_squashed_coupling(mat) =
 
 
 """
-    metabolites(m::MATModel)::Vector{String}
+$(TYPEDSIGNATURES)
 
 Extracts metabolite names from `mets` key in the MAT file.
 """
@@ -47,14 +50,14 @@ function metabolites(m::MATModel)::Vector{String}
 end
 
 """
-    stoichiometry(m::MATModel)
+$(TYPEDSIGNATURES)
 
 Extract the stoichiometry matrix, stored under key `S`.
 """
 stoichiometry(m::MATModel) = sparse(m.mat["S"])
 
 """
-    bounds(m::MATModel)
+$(TYPEDSIGNATURES)
 
 Extracts bounds from the MAT file, saved under `lb` and `ub`.
 """
@@ -64,7 +67,7 @@ bounds(m::MATModel) = (
 )
 
 """
-    balance(m::MATModel)
+$(TYPEDSIGNATURES)
 
 Extracts balance from the MAT model, defaulting to zeroes if not present.
 """
@@ -77,7 +80,7 @@ function balance(m::MATModel)
 end
 
 """
-    objective(m::MATModel)
+$(TYPEDSIGNATURES)
 
 Extracts the objective from the MAT model (defaults to zeroes).
 """
@@ -85,7 +88,7 @@ objective(m::MATModel) =
     sparse(reshape(get(m.mat, "c", zeros(n_reactions(m), 1)), n_reactions(m)))
 
 """
-    coupling(m::MATModel)
+$(TYPEDSIGNATURES)
 
 Extract coupling matrix stored, in `C` key.
 """
@@ -94,7 +97,7 @@ coupling(m::MATModel) =
     sparse(get(m.mat, "C", zeros(0, n_reactions(m))))
 
 """
-    coupling_bounds(m::MATModel)
+$(TYPEDSIGNATURES)
 
 Extracts the coupling constraints. Currently, there are several accepted ways to store these in MATLAB models; this takes the constraints from vectors `cl` and `cu`.
 """
@@ -114,7 +117,7 @@ function coupling_bounds(m::MATModel)
 end
 
 """
-    genes(m::MATModel)
+$(TYPEDSIGNATURES)
 
 Extracts the possible gene list from `genes` key.
 """
@@ -124,7 +127,7 @@ function genes(m::MATModel)
 end
 
 """
-    reaction_gene_association(m::MATModel, rid::String)
+$(TYPEDSIGNATURES)
 
 Extracts the associations from `grRules` key, if present.
 """
@@ -138,7 +141,7 @@ function reaction_gene_association(m::MATModel, rid::String)
 end
 
 """
-    metabolite_formula(m::MATModel, mid::String)
+$(TYPEDSIGNATURES)
 
 Extract metabolite formula from key `metFormula` or `metFormulas`.
 """
@@ -148,7 +151,7 @@ metabolite_formula(m::MATModel, mid::String) = _maybemap(
 )
 
 """
-    metabolite_charge(m::MATModel, mid::String)::Maybe{Int}
+$(TYPEDSIGNATURES)
 
 Extract metabolite charge from `metCharge` or `metCharges`.
 """
@@ -161,7 +164,7 @@ function metabolite_charge(m::MATModel, mid::String)::Maybe{Int}
 end
 
 """
-    metabolite_compartment(m::MATModel, mid::String)
+$(TYPEDSIGNATURES)
 
 Extract metabolite compartment from `metCompartment` or `metCompartments`.
 """
@@ -171,7 +174,7 @@ metabolite_compartment(m::MATModel, mid::String) = _maybemap(
 )
 
 """
-    reaction_stoichiometry(model::MATModel, rid::String)::Dict{String, Float64}
+$(TYPEDSIGNATURES)
 
 Return the stoichiometry of reaction with ID `rid`.
 """
@@ -181,7 +184,7 @@ function reaction_stoichiometry(m::MATModel, rid::String)::Dict{String,Float64}
 end
 
 """
-    reaction_stoichiometry(model::MATModel, ridx)::Dict{String, Float64}
+$(TYPEDSIGNATURES)
 
 Return the stoichiometry of reaction at index `ridx`.
 """
@@ -198,7 +201,7 @@ end
 # out there.
 
 """
-    Base.convert(::Type{MATModel}, m::MetabolicModel)
+$(TYPEDSIGNATURES)
 
 Convert any metabolic model to `MATModel`.
 """
@@ -248,7 +251,7 @@ function Base.convert(::Type{MATModel}, m::MetabolicModel)
 end
 
 """
-    reaction_name(m::MATModel, mid::String)
+$(TYPEDSIGNATURES)
 
 Extract reaction name from `rxnNames`.
 """
@@ -258,7 +261,7 @@ reaction_name(m::MATModel, rid::String) = _maybemap(
 )
 
 """
-    metabolite_name(m::MATModel, mid::String)
+$(TYPEDSIGNATURES)
 
 Extract metabolite name from `metNames`.
 """
diff --git a/src/base/types/MetabolicModel.jl b/src/base/types/MetabolicModel.jl
index ee764cffe..7474b1e61 100644
--- a/src/base/types/MetabolicModel.jl
+++ b/src/base/types/MetabolicModel.jl
@@ -10,7 +10,7 @@
 _missing_impl_error(m, a) = throw(MethodError(m, a))
 
 """
-    reactions(a::MetabolicModel)::Vector{String}
+$(TYPEDSIGNATURES)
 
 Return a vector of reaction identifiers in a model. The vector precisely
 corresponds to the columns in [`stoichiometry`](@ref) matrix.
@@ -26,7 +26,7 @@ function reactions(a::MetabolicModel)::Vector{String}
 end
 
 """
-    metabolites(a::MetabolicModel)::Vector{String}
+$(TYPEDSIGNATURES)
 
 Return a vector of metabolite identifiers in a model. The vector precisely
 corresponds to the rows in [`stoichiometry`](@ref) matrix.
@@ -39,7 +39,7 @@ function metabolites(a::MetabolicModel)::Vector{String}
 end
 
 """
-    n_reactions(a::MetabolicModel)::Int
+$(TYPEDSIGNATURES)
 
 Get the number of reactions in a model.
 """
@@ -48,7 +48,7 @@ function n_reactions(a::MetabolicModel)::Int
 end
 
 """
-    n_metabolites(a::MetabolicModel)::Int
+$(TYPEDSIGNATURES)
 
 Get the number of metabolites in a model.
 """
@@ -57,7 +57,7 @@ function n_metabolites(a::MetabolicModel)::Int
 end
 
 """
-    stoichiometry(a::MetabolicModel)::SparseMat
+$(TYPEDSIGNATURES)
 
 Get the sparse stoichiometry matrix of a model. A feasible solution `x` of a
 model `m` is defined as satisfying the equations:
@@ -72,7 +72,7 @@ function stoichiometry(a::MetabolicModel)::SparseMat
 end
 
 """
-    bounds(a::MetabolicModel)::Tuple{Vector{Float64},Vector{Float64}}
+$(TYPEDSIGNATURES)
 
 Get the lower and upper solution bounds of a model.
 """
@@ -81,7 +81,7 @@ function bounds(a::MetabolicModel)::Tuple{Vector{Float64},Vector{Float64}}
 end
 
 """
-    balance(a::MetabolicModel)::SparseVec
+$(TYPEDSIGNATURES)
 
 Get the sparse balance vector of a model.
 """
@@ -90,7 +90,7 @@ function balance(a::MetabolicModel)::SparseVec
 end
 
 """
-    objective(a::MetabolicModel)::SparseVec
+$(TYPEDSIGNATURES)
 
 Get the objective vector of the model. Analysis functions, such as
 [`flux_balance_analysis`](@ref), are supposed to maximize `dot(objective, x)`
@@ -101,7 +101,7 @@ function objective(a::MetabolicModel)::SparseVec
 end
 
 """
-    fluxes(a::MetabolicModel)::Vector{String}
+$(TYPEDSIGNATURES)
 
 In some models, the [`reactions`](@ref) that correspond to the columns of
 [`stoichiometry`](@ref) matrix do not fully represent the semantic contents of
@@ -121,7 +121,7 @@ function n_fluxes(a::MetabolicModel)::Int
 end
 
 """
-    reaction_flux(a::MetabolicModel)::SparseMat
+$(TYPEDSIGNATURES)
 
 Retrieve a sparse matrix that describes the correspondence of a solution of the
 linear system to the fluxes (see [`fluxes`](@ref) for rationale). Returns a
@@ -136,7 +136,7 @@ function reaction_flux(a::MetabolicModel)::SparseMat
 end
 
 """
-    coupling(a::MetabolicModel)::SparseMat
+$(TYPEDSIGNATURES)
 
 Get a matrix of coupling constraint definitions of a model. By default, there
 is no coupling in the models.
@@ -146,7 +146,7 @@ function coupling(a::MetabolicModel)::SparseMat
 end
 
 """
-    n_coupling_constraints(a::MetabolicModel)::Int
+$(TYPEDSIGNATURES)
 
 Get the number of coupling constraints in a model.
 """
@@ -155,7 +155,7 @@ function n_coupling_constraints(a::MetabolicModel)::Int
 end
 
 """
-    coupling_bounds(a::MetabolicModel)::Tuple{Vector{Float64},Vector{Float64}}
+$(TYPEDSIGNATURES)
 
 Get the lower and upper bounds for each coupling bound in a model, as specified
 by `coupling`. By default, the model does not have any coupling bounds.
@@ -165,7 +165,7 @@ function coupling_bounds(a::MetabolicModel)::Tuple{Vector{Float64},Vector{Float6
 end
 
 """
-    genes(a::MetabolicModel)::Vector{String}
+$(TYPEDSIGNATURES)
 
 Return identifiers of all genes contained in the model. By default, there are
 no genes.
@@ -178,7 +178,7 @@ function genes(a::MetabolicModel)::Vector{String}
 end
 
 """
-    n_genes(a::MetabolicModel)::Int
+$(TYPEDSIGNATURES)
 
 Return the number of genes in the model (as returned by [`genes`](@ref)). If
 you just need the number of the genes, this may be much more efficient than
@@ -189,7 +189,7 @@ function n_genes(a::MetabolicModel)::Int
 end
 
 """
-    reaction_gene_association(a::MetabolicModel, gene_id::String)::Maybe{GeneAssociation}
+$(TYPEDSIGNATURES)
 
 Returns the sets of genes that need to be present so that the reaction can work
 (technically, a DNF on gene availability, with positive atoms only).
@@ -205,7 +205,7 @@ function reaction_gene_association(
 end
 
 """
-    reaction_subsystem(model::MetabolicModel, reaction_id::String)::Maybe{String}
+$(TYPEDSIGNATURES)
 
 Return the subsystem of reaction `reaction_id` in `model` if it is assigned. If not,
 return `nothing`.
@@ -215,7 +215,7 @@ function reaction_subsystem(model::MetabolicModel, reaction_id::String)::Maybe{S
 end
 
 """
-    reaction_stoichiometry(model::MetaboliteModel, rid::String)::Dict{String, Float64}
+$(TYPEDSIGNATURES)
 
 Return the stoichiometry of reaction with ID `rid` in the model. The dictionary
 maps the metabolite IDs to their stoichiometric coefficients.
@@ -229,10 +229,7 @@ function reaction_stoichiometry(m::MetabolicModel, rid::String)::Dict{String,Flo
 end
 
 """
-    metabolite_formula(
-        a::MetabolicModel,
-        metabolite_id::String,
-    )::Maybe{MetaboliteFormula}
+$(TYPEDSIGNATURES)
 
 Return the formula of metabolite `metabolite_id` in `model`.
 Return `nothing` in case the formula is not known or irrelevant.
@@ -245,7 +242,7 @@ function metabolite_formula(
 end
 
 """
-    metabolite_charge(model::MetabolicModel, metabolite_id::String)::Maybe{Int}
+$(TYPEDSIGNATURES)
 
 Return the charge associated with metabolite `metabolite_id` in `model`.
 Returns `nothing` if charge not present.
@@ -255,7 +252,7 @@ function metabolite_charge(model::MetabolicModel, metabolite_id::String)::Maybe{
 end
 
 """
-    metabolite_compartment(model::MetabolicModel, metabolite_id::String)::Maybe{String}
+$(TYPEDSIGNATURES)
 
 Return the compartment of metabolite `metabolite_id` in `model` if it is assigned. If not,
 return `nothing`.
@@ -265,7 +262,7 @@ function metabolite_compartment(model::MetabolicModel, metabolite_id::String)::M
 end
 
 """
-    reaction_annotations(a::MetabolicModel, reaction_id::String)::Annotations
+$(TYPEDSIGNATURES)
 
 Return standardized names that may help identifying the reaction. The
 dictionary assigns vectors of possible identifiers to identifier system names,
@@ -276,7 +273,7 @@ function reaction_annotations(a::MetabolicModel, reaction_id::String)::Annotatio
 end
 
 """
-    metabolite_annotations(a::MetabolicModel, metabolite_id::String)::Annotations
+$(TYPEDSIGNATURES)
 
 Return standardized names that may help to reliably identify the metabolite. The
 dictionary assigns vectors of possible identifiers to identifier system names,
@@ -287,7 +284,7 @@ function metabolite_annotations(a::MetabolicModel, metabolite_id::String)::Annot
 end
 
 """
-    gene_annotations(a::MetabolicModel, gene_id::String)::Annotations
+$(TYPEDSIGNATURES)
 
 Return standardized names that identify the corresponding gene or product. The
 dictionary assigns vectors of possible identifiers to identifier system names,
@@ -298,7 +295,7 @@ function gene_annotations(a::MetabolicModel, gene_id::String)::Annotations
 end
 
 """
-    reaction_notes(model::MetabolicModel, reaction_id::String)::Notes
+$(TYPEDSIGNATURES)
 
 Return the notes associated with reaction `reaction_id` in `model`.
 """
@@ -307,7 +304,7 @@ function reaction_notes(model::MetabolicModel, reaction_id::String)::Notes
 end
 
 """
-    metabolite_notes(model::MetabolicModel, metabolite_id::String)::Notes
+$(TYPEDSIGNATURES)
 
 Return the notes associated with metabolite `reaction_id` in `model`.
 """
@@ -316,7 +313,7 @@ function metabolite_notes(model::MetabolicModel, metabolite_id::String)::Notes
 end
 
 """
-    gene_notes(model::MetabolicModel, gene_id::String)::Notes
+$(TYPEDSIGNATURES)
 
 Return the notes associated with the gene `gene_id` in `model`.
 """
@@ -325,28 +322,28 @@ function gene_notes(model::MetabolicModel, gene_id::String)::Notes
 end
 
 """
-    reaction_name(model::MetabolicModel, rid::String)
+$(TYPEDSIGNATURES)
 
 Return the name of reaction with ID `rid`.
 """
 reaction_name(model::MetabolicModel, rid::String) = nothing
 
 """
-    metabolite_name(model::MetabolicModel, mid::String)
+$(TYPEDSIGNATURES)
 
 Return the name of metabolite with ID `mid`.
 """
 metabolite_name(model::MetabolicModel, mid::String) = nothing
 
 """
-    gene_name(model::MetabolicModel, gid::String)
+$(TYPEDSIGNATURES)
 
 Return the name of gene with ID `gid`.
 """
 gene_name(model::MetabolicModel, gid::String) = nothing
 
 """
-    precache!(a::MetabolicModel)::Nothing
+$(TYPEDSIGNATURES)
 
 Do whatever is feasible to get the model into a state that can be read from
 as-quickly-as-possible. This may include e.g. generating helper index
diff --git a/src/base/types/Metabolite.jl b/src/base/types/Metabolite.jl
index d6b195d7b..79bb0330a 100644
--- a/src/base/types/Metabolite.jl
+++ b/src/base/types/Metabolite.jl
@@ -1,16 +1,8 @@
 """
-Metabolite structure.
+$(TYPEDEF)
 
 # Fields
-````
-id :: String
-name :: Maybe{String}
-formula :: String
-charge :: Int
-compartment :: String
-notes :: Dict{String, Vector{String}}
-annotation :: Dict{String, Union{Vector{String}, String}}
-````
+$(TYPEDFIELDS)
 """
 mutable struct Metabolite
     id::String
@@ -20,14 +12,19 @@ mutable struct Metabolite
     compartment::Maybe{String}
     notes::Notes
     annotations::Annotations
-
-    Metabolite(
-        id = "";
-        name = nothing,
-        formula = nothing,
-        charge = nothing,
-        compartment = nothing,
-        notes = Notes(),
-        annotations = Annotations(),
-    ) = new(id, name, formula, charge, compartment, notes, annotations)
 end
+
+"""
+$(TYPEDSIGNATURES)
+
+A constructor for `Metabolite`s.
+"""
+Metabolite(
+    id = "";
+    name = nothing,
+    formula = nothing,
+    charge = nothing,
+    compartment = nothing,
+    notes = Notes(),
+    annotations = Annotations(),
+) = Metabolite(String(id), name, formula, charge, compartment, notes, annotations)
diff --git a/src/base/types/ModelWrapper.jl b/src/base/types/ModelWrapper.jl
index ccf289d3e..e1988f1ae 100644
--- a/src/base/types/ModelWrapper.jl
+++ b/src/base/types/ModelWrapper.jl
@@ -1,6 +1,6 @@
 
 """
-    unwrap_model(a::ModelWrapper)
+$(TYPEDSIGNATURES)
 
 A simple helper to pick the single w
 """
diff --git a/src/base/types/Reaction.jl b/src/base/types/Reaction.jl
index e23bc4086..b87e1c45c 100644
--- a/src/base/types/Reaction.jl
+++ b/src/base/types/Reaction.jl
@@ -1,18 +1,10 @@
 """
-    mutable struct Reaction
-        id::String
-        name::Maybe{String}
-        metabolites::Dict{String,Float64}
-        lb::Float64
-        ub::Float64
-        grr::Maybe{GeneAssociation}
-        subsystem::Maybe{String}
-        notes::Notes
-        annotations::Annotations
-        objective_coefficient::Float64
-    end
+$(TYPEDEF)
 
 A structure for representing a single reaction in a [`StandardModel`](@ref).
+
+# Fields
+$(TYPEDFIELDS)
 """
 mutable struct Reaction
     id::String
@@ -28,18 +20,7 @@ mutable struct Reaction
 end
 
 """
-    Reaction(
-        id = "";
-        name = nothing,
-        metabolites = Dict{String,Float64}(),
-        lb = -_constants.default_reaction_bound,
-        ub = _constants.default_reaction_bound,
-        grr = nothing,
-        subsystem = nothing,
-        notes = Notes(),
-        annotations = Annotations(),
-        objective_coefficient = 0.0,
-    )
+$(TYPEDSIGNATURES)
 
 A constructor for Reaction that only takes a reaction `id` and
 assigns default/uninformative values to all the fields that are not
@@ -73,12 +54,7 @@ function Reaction(
 end
 
 """
-    Reaction(
-        id::String,
-        metabolites::Dict{String,Union{Int, Float64}},
-        dir = :bidirectional;
-        default_bound = _constants.default_reaction_bound,
-    )
+$(TYPEDSIGNATURES)
 
 Convenience constructor for `Reaction`. The reaction equation is specified using
 `metabolites`, which is a dictionary mapping metabolite ids to stoichiometric
diff --git a/src/base/types/ReactionStatus.jl b/src/base/types/ReactionStatus.jl
index 0ca1da7b0..90940711b 100644
--- a/src/base/types/ReactionStatus.jl
+++ b/src/base/types/ReactionStatus.jl
@@ -1,5 +1,10 @@
 """
+$(TYPEDEF)
+
 Used for concise reporting of modeling results.
+
+# Fields
+$(TYPEDFIELDS)
 """
 mutable struct ReactionStatus
     already_present::Bool
diff --git a/src/base/types/SBMLModel.jl b/src/base/types/SBMLModel.jl
index d15536f67..fe14efda8 100644
--- a/src/base/types/SBMLModel.jl
+++ b/src/base/types/SBMLModel.jl
@@ -1,43 +1,46 @@
 """
-    struct SBMLModel
+$(TYPEDEF)
 
 Thin wrapper around the model from SBML.jl library. Allows easy conversion from
 SBML to any other model format.
+
+# Fields
+$(TYPEDFIELDS)
 """
 struct SBMLModel <: MetabolicModel
     sbml::SBML.Model
 end
 
 """
-    reactions(model::SBMLModel)::Vector{String}
+$(TYPEDSIGNATURES)
 
 Get reactions from a [`SBMLModel`](@ref).
 """
 reactions(model::SBMLModel)::Vector{String} = [k for k in keys(model.sbml.reactions)]
 
 """
-    metabolites(model::SBMLModel)::Vector{String}
+$(TYPEDSIGNATURES)
 
 Get metabolites from a [`SBMLModel`](@ref).
 """
 metabolites(model::SBMLModel)::Vector{String} = [k for k in keys(model.sbml.species)]
 
 """
-    n_reactions(model::SBMLModel)::Int
+$(TYPEDSIGNATURES)
 
 Efficient counting of reactions in [`SBMLModel`](@ref).
 """
 n_reactions(model::SBMLModel)::Int = length(model.sbml.reactions)
 
 """
-    n_metabolites(model::SBMLModel)::Int
+$(TYPEDSIGNATURES)
 
 Efficient counting of metabolites in [`SBMLModel`](@ref).
 """
 n_metabolites(model::SBMLModel)::Int = length(model.sbml.species)
 
 """
-    stoichiometry(model::SBMLModel)::SparseMat
+$(TYPEDSIGNATURES)
 
 Recreate the stoichiometry matrix from the [`SBMLModel`](@ref).
 """
@@ -47,7 +50,7 @@ function stoichiometry(model::SBMLModel)::SparseMat
 end
 
 """
-    bounds(model::SBMLModel)::Tuple{Vector{Float64},Vector{Float64}}
+$(TYPEDSIGNATURES)
 
 Get the lower and upper flux bounds of model [`SBMLModel`](@ref). Throws `DomainError` in
 case if the SBML contains mismatching units.
@@ -71,35 +74,35 @@ function bounds(model::SBMLModel)::Tuple{Vector{Float64},Vector{Float64}}
 end
 
 """
-    balance(model::SBMLModel)::SparseVec
+$(TYPEDSIGNATURES)
 
 Balance vector of a [`SBMLModel`](@ref). This is always zero.
 """
 balance(model::SBMLModel)::SparseVec = spzeros(n_metabolites(model))
 
 """
-    objective(model::SBMLModel)::SparseVec
+$(TYPEDSIGNATURES)
 
 Objective of the [`SBMLModel`](@ref).
 """
 objective(model::SBMLModel)::SparseVec = SBML.flux_objective(model.sbml)
 
 """
-    genes(model::SBMLModel)::Vector{String}
+$(TYPEDSIGNATURES)
 
 Get genes of a [`SBMLModel`](@ref).
 """
 genes(model::SBMLModel)::Vector{String} = [k for k in keys(model.sbml.gene_products)]
 
 """
-    n_genes(model::SBMLModel)::Int
+$(TYPEDSIGNATURES)
 
 Get number of genes in [`SBMLModel`](@ref).
 """
 n_genes(model::SBMLModel)::Int = length(model.sbml.gene_products)
 
 """
-    reaction_gene_association(model::SBMLModel, rid::String)::Maybe{GeneAssociation}
+$(TYPEDSIGNATURES)
 
 Retrieve the [`GeneAssociation`](@ref) from [`SBMLModel`](@ref).
 """
@@ -107,7 +110,7 @@ reaction_gene_association(model::SBMLModel, rid::String)::Maybe{GeneAssociation}
     _maybemap(_parse_grr, model.sbml.reactions[rid].gene_product_association)
 
 """
-    metabolite_formula(model::SBMLModel, mid::String)::Maybe{MetaboliteFormula}
+$(TYPEDSIGNATURES)
 
 Get [`MetaboliteFormula`](@ref) from a chosen metabolite from [`SBMLModel`](@ref).
 """
@@ -115,36 +118,71 @@ metabolite_formula(model::SBMLModel, mid::String)::Maybe{MetaboliteFormula} =
     _maybemap(_parse_formula, model.sbml.species[mid].formula)
 
 """
-    metabolite_compartment(model::SBMLModel, mid::String)::Maybe{String}
+$(TYPEDSIGNATURES)
 
 Get the compartment of a chosen metabolite from [`SBMLModel`](@ref).
 """
 metabolite_compartment(model::SBMLModel, mid::String) = model.sbml.species[mid].compartment
 
 """
-    metabolite_charge(model::SBMLModel, mid::String)::Maybe{Int}
+$(TYPEDSIGNATURES)
 
 Get charge of a chosen metabolite from [`SBMLModel`](@ref).
 """
 metabolite_charge(model::SBMLModel, mid::String)::Maybe{Int} =
     model.sbml.species[mid].charge
 
-function _sbml_export_annotation(annotation)::Maybe{String}
-    if isnothing(annotation) || isempty(annotation)
-        nothing
-    elseif length(annotation) != 1 || first(annotation).first != ""
-        @_io_log @warn "Possible data loss: multiple annotations converted to text for SBML" annotation
-        join(["$k: $v" for (k, v) in annotation], "\n")
-    else
-        @_io_log @warn "Possible data loss: trying to represent annotation in SBML is unlikely to work " annotation
-        first(annotation).second
+function _parse_sbml_identifiers_org_uri(uri::String)::Tuple{String,String}
+    m = match(r"^http://identifiers.org/([^/]+)/(.*)$", uri)
+    isnothing(m) ? ("RESOURCE_URI", uri) : (m[1], m[2])
+end
+
+function _sbml_import_cvterms(sbo::Maybe{String}, cvs::Vector{SBML.CVTerm})::Annotations
+    res = Annotations()
+    isnothing(sbo) || (res["sbo"] = [sbo])
+    for cv in cvs
+        cv.biological_qualifier == :is || continue
+        for (id, val) in _parse_sbml_identifiers_org_uri.(cv.resource_uris)
+            push!(get!(res, id, []), val)
+        end
+    end
+    return res
+end
+
+function _sbml_export_cvterms(annotations::Annotations)::Vector{SBML.CVTerm}
+    isempty(annotations) && return []
+    length(annotations) == 1 && haskey(annotations, "sbo") && return []
+    [
+        SBML.CVTerm(
+            biological_qualifier = :is,
+            resource_uris = [
+                id == "RESOURCE_URI" ? val : "http://identifiers.org/$id/$val" for
+                (id, vals) = annotations if id != "sbo" for val in vals
+            ],
+        ),
+    ]
+end
+
+function _sbml_export_sbo(annotations::Annotations)::Maybe{String}
+    haskey(annotations, "sbo") || return nothing
+    if length(annotations["sbo"]) != 1
+        @_io_log @error "Data loss: SBO term is not unique for SBML export" annotations["sbo"]
+        return
     end
+    return annotations["sbo"][1]
 end
 
-const _sbml_export_notes = _sbml_export_annotation
+function _sbml_import_notes(notes::Maybe{String})::Notes
+    isnothing(notes) ? Notes() : Notes("" => [notes])
+end
+
+function _sbml_export_notes(notes::Notes)::Maybe{String}
+    isempty(notes) || @_io_log @error "Data loss: notes not exported to SBML" notes
+    nothing
+end
 
 """
-    reaction_stoichiometry(model::SBMLModel, rid::String)::Dict{String, Float64}
+$(TYPEDSIGNATURES)
 
 Return the stoichiometry of reaction with ID `rid`.
 """
@@ -161,28 +199,78 @@ function reaction_stoichiometry(m::SBMLModel, rid::String)::Dict{String,Float64}
 end
 
 """
-    reaction_name(model::SBMLModel, rid::String)
+$(TYPEDSIGNATURES)
 
 Return the name of reaction with ID `rid`.
 """
 reaction_name(model::SBMLModel, rid::String) = model.sbml.reactions[rid].name
 
 """
-    metabolite_name(model::SBMLModel, mid::String)
+$(TYPEDSIGNATURES)
 
 Return the name of metabolite with ID `mid`.
 """
 metabolite_name(model::SBMLModel, mid::String) = model.sbml.species[mid].name
 
 """
-    gene_name(model::SBMLModel, gid::String)
+$(TYPEDSIGNATURES)
 
 Return the name of gene with ID `gid`.
 """
 gene_name(model::SBMLModel, gid::String) = model.sbml.gene_products[gid].name
 
 """
-    Base.convert(::Type{SBMLModel}, mm::MetabolicModel)
+$(TYPEDSIGNATURES)
+
+Return the annotations of reaction with ID `rid`.
+"""
+reaction_annotations(model::SBMLModel, rid::String) =
+    _sbml_import_cvterms(model.sbml.reactions[rid].sbo, model.sbml.reactions[rid].cv_terms)
+
+"""
+$(TYPEDSIGNATURES)
+
+Return the annotations of metabolite with ID `mid`.
+"""
+metabolite_annotations(model::SBMLModel, mid::String) =
+    _sbml_import_cvterms(model.sbml.species[mid].sbo, model.sbml.species[mid].cv_terms)
+
+"""
+$(TYPEDSIGNATURES)
+
+Return the annotations of gene with ID `gid`.
+"""
+gene_annotations(model::SBMLModel, gid::String) = _sbml_import_cvterms(
+    model.sbml.gene_products[gid].sbo,
+    model.sbml.gene_products[gid].cv_terms,
+)
+
+"""
+$(TYPEDSIGNATURES)
+
+Return the notes about reaction with ID `rid`.
+"""
+reaction_notes(model::SBMLModel, rid::String) =
+    _sbml_import_notes(model.sbml.reactions[rid].notes)
+
+"""
+$(TYPEDSIGNATURES)
+
+Return the notes about metabolite with ID `mid`.
+"""
+metabolite_notes(model::SBMLModel, mid::String) =
+    _sbml_import_notes(model.sbml.species[mid].notes)
+
+"""
+$(TYPEDSIGNATURES)
+
+Return the notes about gene with ID `gid`.
+"""
+gene_notes(model::SBMLModel, gid::String) =
+    _sbml_import_notes(model.sbml.gene_products[gid].notes)
+
+"""
+$(TYPEDSIGNATURES)
 
 Convert any metabolic model to [`SBMLModel`](@ref).
 """
@@ -198,30 +286,36 @@ function Base.convert(::Type{SBMLModel}, mm::MetabolicModel)
     comps = _default.("compartment", metabolite_compartment.(Ref(mm), mets))
     compss = Set(comps)
 
+    metid(x) = startswith(x, "M_") ? x : "M_$x"
+    rxnid(x) = startswith(x, "R_") ? x : "R_$x"
+    gprid(x) = startswith(x, "G_") ? x : "G_$x"
+
     return SBMLModel(
         SBML.Model(
             compartments = Dict(
                 comp => SBML.Compartment(constant = true) for comp in compss
             ),
             species = Dict(
-                mid => SBML.Species(
+                metid(mid) => SBML.Species(
                     name = metabolite_name(mm, mid),
                     compartment = _default("compartment", comps[mi]),
-                    formula = metabolite_formula(mm, mid),
+                    formula = _maybemap(_unparse_formula, metabolite_formula(mm, mid)),
                     charge = metabolite_charge(mm, mid),
                     constant = false,
                     boundary_condition = false,
                     only_substance_units = false,
+                    sbo = _sbml_export_sbo(metabolite_annotations(mm, mid)),
                     notes = _sbml_export_notes(metabolite_notes(mm, mid)),
-                    annotation = _sbml_export_annotation(metabolite_annotations(mm, mid)),
+                    metaid = metid(mid),
+                    cv_terms = _sbml_export_cvterms(metabolite_annotations(mm, mid)),
                 ) for (mi, mid) in enumerate(mets)
             ),
             reactions = Dict(
-                rid => SBML.Reaction(
+                rxnid(rid) => SBML.Reaction(
                     name = reaction_name(mm, rid),
                     reactants = [
                         SBML.SpeciesReference(
-                            species = mets[i],
+                            species = metid(mets[i]),
                             stoichiometry = -stoi[i, ri],
                             constant = true,
                         ) for
@@ -229,7 +323,7 @@ function Base.convert(::Type{SBMLModel}, mm::MetabolicModel)
                     ],
                     products = [
                         SBML.SpeciesReference(
-                            species = mets[i],
+                            species = metid(mets[i]),
                             stoichiometry = stoi[i, ri],
                             constant = true,
                         ) for
@@ -246,16 +340,20 @@ function Base.convert(::Type{SBMLModel}, mm::MetabolicModel)
                         reaction_gene_association(mm, rid),
                     ),
                     reversible = true,
+                    sbo = _sbml_export_sbo(reaction_annotations(mm, rid)),
                     notes = _sbml_export_notes(reaction_notes(mm, rid)),
-                    annotation = _sbml_export_annotation(reaction_annotations(mm, rid)),
+                    metaid = rxnid(rid),
+                    cv_terms = _sbml_export_cvterms(reaction_annotations(mm, rid)),
                 ) for (ri, rid) in enumerate(rxns)
             ),
             gene_products = Dict(
-                gid => SBML.GeneProduct(
+                gprid(gid) => SBML.GeneProduct(
                     label = gid,
                     name = gene_name(mm, gid),
+                    sbo = _sbml_export_sbo(gene_annotations(mm, gid)),
                     notes = _sbml_export_notes(gene_notes(mm, gid)),
-                    annotation = _sbml_export_annotation(gene_annotations(mm, gid)),
+                    metaid = gprid(gid),
+                    cv_terms = _sbml_export_cvterms(gene_annotations(mm, gid)),
                 ) for gid in genes(mm)
             ),
             active_objective = "objective",
diff --git a/src/base/types/Serialized.jl b/src/base/types/Serialized.jl
index 1d1741816..aef1c887c 100644
--- a/src/base/types/Serialized.jl
+++ b/src/base/types/Serialized.jl
@@ -1,13 +1,13 @@
 
 """
-    mutable struct Serialized{M <: MetabolicModel}
-        m::Maybe{M}
-        filename::String
-    end
+$(TYPEDEF)
 
 A meta-model that represents a model that is serialized on the disk. The
 internal model will be loaded on-demand by using any accessor, or by calling
 [`precache!`](@ref) directly.
+
+# Fields
+$(TYPEDFIELDS)
 """
 mutable struct Serialized{M} <: ModelWrapper where {M<:MetabolicModel}
     m::Maybe{M}
@@ -19,7 +19,7 @@ mutable struct Serialized{M} <: ModelWrapper where {M<:MetabolicModel}
 end
 
 """
-    unwrap_model(m::Serialized)
+$(TYPEDSIGNATURES)
 
 Unwrap the serialized model (precaching it transparently).
 """
@@ -29,7 +29,7 @@ function unwrap_model(m::Serialized)
 end
 
 """
-    precache!(model::Serialized{MetabolicModel})::Nothing
+$(TYPEDSIGNATURES)
 
 Load the `Serialized` model from disk in case it's not alreadly loaded.
 """
diff --git a/src/base/types/StandardModel.jl b/src/base/types/StandardModel.jl
index e842525e9..211a1e088 100644
--- a/src/base/types/StandardModel.jl
+++ b/src/base/types/StandardModel.jl
@@ -1,5 +1,5 @@
 """
-    mutable struct StandardModel
+$(TYPEDEF)
 
 `StandardModel` is used to store a constraint based metabolic model with
 meta-information.  Meta-information is defined as annotation details, which
@@ -26,19 +26,14 @@ types if performance is critical.
 
 See also: [`Reaction`](@ref), [`Metabolite`](@ref), [`Gene`](@ref)
 
-# Fields
-```
-id :: String
-reactions :: OrderedDict{String, Reaction}
-metabolites :: OrderedDict{String, Metabolite}
-genes :: OrderedDict{String, Gene}
-```
-
 # Example
 ```
 model = load_model(StandardModel, "my_model.json")
 keys(model.reactions)
 ```
+
+# Fields
+$(TYPEDFIELDS)
 """
 mutable struct StandardModel <: MetabolicModel
     id::String
@@ -56,7 +51,7 @@ end
 
 # MetabolicModel interface follows
 """
-    reactions(model::StandardModel)
+$(TYPEDSIGNATURES)
 
 Return a vector of reaction id strings contained in `model`.
 The order of reaction ids returned here matches the order used to construct the
@@ -65,7 +60,7 @@ stoichiometric matrix.
 reactions(model::StandardModel)::StringVecType = collect(keys(model.reactions))
 
 """
-    n_reactions(model::StandardModel)
+$(TYPEDSIGNATURES)
 
 Return the number of reactions contained in `model`.
 """
@@ -73,7 +68,7 @@ n_reactions(model::StandardModel)::Int = length(model.reactions)
 
 
 """
-    metabolites(model::StandardModel)
+$(TYPEDSIGNATURES)
 
 Return a vector of metabolite id strings contained in `model`.
 The order of metabolite strings returned here matches the order used to construct
@@ -82,28 +77,28 @@ the stoichiometric matrix.
 metabolites(model::StandardModel)::StringVecType = collect(keys(model.metabolites))
 
 """
-n_metabolites(model::StandardModel)
+$(TYPEDSIGNATURES)
 
 Return the number of metabolites in `model`.
 """
 n_metabolites(model::StandardModel)::Int = length(model.metabolites)
 
 """
-    genes(model::StandardModel)
+$(TYPEDSIGNATURES)
 
 Return a vector of gene id strings in `model`.
 """
 genes(model::StandardModel)::StringVecType = collect(keys(model.genes))
 
 """
-    n_genes(model::StandardModel)
+$(TYPEDSIGNATURES)
 
 Return the number of genes in `model`.
 """
 n_genes(model::StandardModel)::Int = length(model.genes)
 
 """
-    stoichiometry(model::StandardModel)
+$(TYPEDSIGNATURES)
 
 Return the stoichiometric matrix associated with `model` in sparse format.
 """
@@ -144,7 +139,7 @@ function stoichiometry(model::StandardModel)::SparseMat
 end
 
 """
-    lower_bounds(model::StandardModel)::Vector{Float64}
+$(TYPEDSIGNATURES)
 
 Return the lower bounds for all reactions in `model` in sparse format.
 """
@@ -152,7 +147,7 @@ lower_bounds(model::StandardModel)::Vector{Float64} =
     sparse([model.reactions[rxn].lb for rxn in reactions(model)])
 
 """
-    upper_bounds(model::StandardModel)::Vector{Float64}
+$(TYPEDSIGNATURES)
 
 Return the upper bounds for all reactions in `model` in sparse format.
 Order matches that of the reaction ids returned in `reactions()`.
@@ -161,7 +156,7 @@ upper_bounds(model::StandardModel)::Vector{Float64} =
     sparse([model.reactions[rxn].ub for rxn in reactions(model)])
 
 """
-    bounds(model::StandardModel)::Tuple{Vector{Float64},Vector{Float64}}
+$(TYPEDSIGNATURES)
 
 Return the lower and upper bounds, respectively, for reactions in `model`.
 Order matches that of the reaction ids returned in `reactions()`.
@@ -170,7 +165,7 @@ bounds(model::StandardModel)::Tuple{Vector{Float64},Vector{Float64}} =
     (lower_bounds(model), upper_bounds(model))
 
 """
-    balance(model::StandardModel)
+$(TYPEDSIGNATURES)
 
 Return the balance of the linear problem, i.e. b in Sv = 0 where S is the stoichiometric matrix
 and v is the flux vector.
@@ -178,7 +173,7 @@ and v is the flux vector.
 balance(model::StandardModel)::SparseVec = spzeros(length(model.metabolites))
 
 """
-    objective(model::StandardModel)
+$(TYPEDSIGNATURES)
 
 Return sparse objective vector for `model`.
 """
@@ -186,7 +181,7 @@ objective(model::StandardModel)::SparseVec =
     sparse([model.reactions[rid].objective_coefficient for rid in keys(model.reactions)])
 
 """
-    reaction_gene_association(model::StandardModel, id::String)
+$(TYPEDSIGNATURES)
 
 Return the gene reaction rule in string format for reaction with `id` in `model`.
 Return `nothing` if not available.
@@ -195,7 +190,7 @@ reaction_gene_association(model::StandardModel, id::String)::Maybe{GeneAssociati
     _maybemap(identity, model.reactions[id].grr)
 
 """
-    metabolite_formula(model::StandardModel, id::String)
+$(TYPEDSIGNATURES)
 
 Return the formula of reaction `id` in `model`.
 Return `nothing` if not present.
@@ -204,7 +199,7 @@ metabolite_formula(model::StandardModel, id::String)::Maybe{MetaboliteFormula} =
     _maybemap(_parse_formula, model.metabolites[id].formula)
 
 """
-    metabolite_charge(model::StandardModel, id::String)
+$(TYPEDSIGNATURES)
 
 Return the charge associated with metabolite `id` in `model`.
 Return nothing if not present.
@@ -213,7 +208,7 @@ metabolite_charge(model::StandardModel, id::String)::Maybe{Int} =
     model.metabolites[id].charge
 
 """
-    metabolite_compartment(model::StandardModel, id::String)
+$(TYPEDSIGNATURES)
 
 Return compartment associated with metabolite `id` in `model`.
 Return `nothing` if not present.
@@ -222,7 +217,7 @@ metabolite_compartment(model::StandardModel, id::String)::Maybe{String} =
     model.metabolites[id].compartment
 
 """
-    reaction_subsystem(id::String, model::StandardModel)
+$(TYPEDSIGNATURES)
 
 Return the subsystem associated with reaction `id` in `model`.
 Return `nothing` if not present.
@@ -231,7 +226,7 @@ reaction_subsystem(model::StandardModel, id::String)::Maybe{String} =
     model.reactions[id].subsystem
 
 """
-    metabolite_notes(model::StandardModel, id::String)::Notes
+$(TYPEDSIGNATURES)
 
 Return the notes associated with metabolite `id` in `model`.
 Return an empty Dict if not present.
@@ -240,7 +235,7 @@ metabolite_notes(model::StandardModel, id::String)::Maybe{Notes} =
     model.metabolites[id].notes
 
 """
-    metabolite_annotations(model::StandardModel, id::String)::Annotations
+$(TYPEDSIGNATURES)
 
 Return the annotation associated with metabolite `id` in `model`.
 Return an empty Dict if not present.
@@ -249,7 +244,7 @@ metabolite_annotations(model::StandardModel, id::String)::Maybe{Annotations} =
     model.metabolites[id].annotations
 
 """
-    gene_notes(model::StandardModel, id::String)::Notes
+$(TYPEDSIGNATURES)
 
 Return the notes associated with gene `id` in `model`.
 Return an empty Dict if not present.
@@ -257,7 +252,7 @@ Return an empty Dict if not present.
 gene_notes(model::StandardModel, id::String)::Maybe{Notes} = model.genes[id].notes
 
 """
-    gene_annotations(model::StandardModel, id::String)::Annotations
+$(TYPEDSIGNATURES)
 
 Return the annotation associated with gene `id` in `model`.
 Return an empty Dict if not present.
@@ -266,7 +261,7 @@ gene_annotations(model::StandardModel, id::String)::Maybe{Annotations} =
     model.genes[id].annotations
 
 """
-    reaction_notes(model::StandardModel, id::String)::Notes
+$(TYPEDSIGNATURES)
 
 Return the notes associated with reaction `id` in `model`.
 Return an empty Dict if not present.
@@ -274,7 +269,7 @@ Return an empty Dict if not present.
 reaction_notes(model::StandardModel, id::String)::Maybe{Notes} = model.reactions[id].notes
 
 """
-    reaction_annotations(model::StandardModel, id::String)::Annotations
+$(TYPEDSIGNATURES)
 
 Return the annotation associated with reaction `id` in `model`.
 Return an empty Dict if not present.
@@ -283,7 +278,7 @@ reaction_annotations(model::StandardModel, id::String)::Maybe{Annotations} =
     model.reactions[id].annotations
 
 """
-    reaction_stoichiometry(model::StandardModel, rid::String)::Dict{String, Float64}
+$(TYPEDSIGNATURES)
 
 Return the stoichiometry of reaction with ID `rid`.
 """
@@ -291,28 +286,28 @@ reaction_stoichiometry(m::StandardModel, rid::String)::Dict{String,Float64} =
     m.reactions[rid].metabolites
 
 """
-    reaction_name(m::StandardModel, rid::String)
+$(TYPEDSIGNATURES)
 
 Return the name of reaction with ID `id`.
 """
 reaction_name(m::StandardModel, rid::String) = m.reactions[rid].name
 
 """
-    metabolite_name(m::StandardModel, mid::String)
+$(TYPEDSIGNATURES)
 
 Return the name of metabolite with ID `id`.
 """
 metabolite_name(m::StandardModel, mid::String) = m.metabolites[mid].name
 
 """
-    gene_name(m::StandardModel, gid::String)
+$(TYPEDSIGNATURES)
 
 Return the name of gene with ID `id`.
 """
 gene_name(m::StandardModel, gid::String) = m.genes[gid].name
 
 """
-Base.convert(::Type{StandardModel}, model::MetabolicModel)
+$(TYPEDSIGNATURES)
 
 Convert any `MetabolicModel` into a `StandardModel`.
 Note, some data loss may occur since only the generic interface is used during
diff --git a/src/base/types/abstract/Maybe.jl b/src/base/types/abstract/Maybe.jl
index ca5f092b6..7b5df0a39 100644
--- a/src/base/types/abstract/Maybe.jl
+++ b/src/base/types/abstract/Maybe.jl
@@ -7,7 +7,7 @@ A nice name for "nullable" type.
 const Maybe{T} = Union{Nothing,T}
 
 """
-    _default(d::T, x::Maybe{T})::T where {T}
+$(TYPEDSIGNATURES)
 
 Fold the `Maybe{T}` down to `T` by defaulting.
 """
@@ -16,7 +16,7 @@ function _default(d::T, x::Maybe{T})::T where {T}
 end
 
 """
-    _maybemap(f, x::Maybe)::Maybe
+$(TYPEDSIGNATURES)
 
 Apply a function to `x` only if it is not `nothing`.
 """
diff --git a/src/base/types/wrappers/GeckoModel.jl b/src/base/types/wrappers/GeckoModel.jl
index 4b12951d2..dace278d7 100644
--- a/src/base/types/wrappers/GeckoModel.jl
+++ b/src/base/types/wrappers/GeckoModel.jl
@@ -1,7 +1,10 @@
 """
-    struct _gecko_reaction_column
+$(TYPEDEF)
 
 A helper type for describing the contents of [`GeckoModel`](@ref)s.
+
+# Fields
+$(TYPEDFIELDS)
 """
 struct _gecko_reaction_column
     reaction_idx::Int
@@ -14,10 +17,13 @@ struct _gecko_reaction_column
 end
 
 """
-    struct _gecko_capacity
+$(TYPEDEF)
 
 A helper struct that contains the gene product capacity terms organized by
 the grouping type, e.g. metabolic or membrane groups etc.
+
+# Fields
+$(TYPEDFIELDS)
 """
 struct _gecko_capacity
     group_id::String
@@ -27,7 +33,7 @@ struct _gecko_capacity
 end
 
 """
-    struct GeckoModel <: ModelWrapper
+$(TYPEDEF)
 
 A model with complex enzyme concentration and capacity bounds, as described in
 *Sánchez, Benjamín J., et al. "Improving the phenotype predictions of a yeast
@@ -64,6 +70,9 @@ Implementation exposes the split reactions (available as `reactions(model)`),
 but retains the original "simple" reactions accessible by [`fluxes`](@ref).
 The related constraints are implemented using [`coupling`](@ref) and
 [`coupling_bounds`](@ref).
+
+# Fields
+$(TYPEDFIELDS)
 """
 struct GeckoModel <: ModelWrapper
     objective::SparseVec
@@ -78,7 +87,7 @@ end
 unwrap_model(model::GeckoModel) = model.inner
 
 """
-    stoichiometry(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 Return a stoichiometry of the [`GeckoModel`](@ref). The enzymatic reactions are
 split into unidirectional forward and reverse ones, each of which may have
@@ -94,7 +103,7 @@ function stoichiometry(model::GeckoModel)
 end
 
 """
-    objective(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 Return the objective of the [`GeckoModel`](@ref). Note, the objective is with
 respect to the internal variables, i.e. [`reactions(model)`](@ref), which are
@@ -104,7 +113,7 @@ have kinetic data.
 objective(model::GeckoModel) = model.objective
 
 """
-    reactions(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 Returns the internal reactions in a [`GeckoModel`](@ref) (these may be split
 to forward- and reverse-only parts with different isozyme indexes; reactions
@@ -123,7 +132,7 @@ function reactions(model::GeckoModel)
 end
 
 """
-    n_reactions(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 Returns the number of all irreversible reactions in `model` as well as the
 number of gene products that take part in enzymatic reactions.
@@ -131,7 +140,7 @@ number of gene products that take part in enzymatic reactions.
 n_reactions(model::GeckoModel) = length(model.columns) + n_genes(model)
 
 """
-    bounds(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 Return variable bounds for [`GeckoModel`](@ref).
 """
@@ -148,7 +157,7 @@ function bounds(model::GeckoModel)
 end
 
 """
-    reaction_flux(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 Get the mapping of the reaction rates in [`GeckoModel`](@ref) to the original
 fluxes in the wrapped model.
@@ -162,7 +171,7 @@ function reaction_flux(model::GeckoModel)
 end
 
 """
-    coupling(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 Return the coupling of [`GeckoModel`](@ref). That combines the coupling of the
 wrapped model, coupling for split (arm) reactions, and the coupling for the total
@@ -180,7 +189,7 @@ function coupling(model::GeckoModel)
 end
 
 """
-    n_coupling_constraints(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 Count the coupling constraints in [`GeckoModel`](@ref) (refer to
 [`coupling`](@ref) for details).
@@ -191,7 +200,7 @@ n_coupling_constraints(model::GeckoModel) =
     length(model.coupling_row_mass_group)
 
 """
-    coupling_bounds(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 The coupling bounds for [`GeckoModel`](@ref) (refer to [`coupling`](@ref) for
 details).
@@ -214,7 +223,7 @@ function coupling_bounds(model::GeckoModel)
 end
 
 """
-    balance(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 Return the balance of the reactions in the inner model, concatenated with a vector of
 zeros representing the enzyme balance of a [`GeckoModel`](@ref).
@@ -223,14 +232,14 @@ balance(model::GeckoModel) =
     [balance(model.inner); spzeros(length(model.coupling_row_gene_product))]
 
 """
-    n_genes(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 Return the number of genes that have enzymatic constraints associated with them.
 """
 n_genes(model::GeckoModel) = length(model.coupling_row_gene_product)
 
 """
-    genes(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 Return the gene ids of genes that have enzymatic constraints associated with them.
 """
@@ -238,14 +247,14 @@ genes(model::GeckoModel) =
     genes(model.inner)[[idx for (idx, _) in model.coupling_row_gene_product]]
 
 """
-    metabolites(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 Return the ids of all metabolites, both real and pseudo, for a [`GeckoModel`](@ref).
 """
 metabolites(model::GeckoModel) = [metabolites(model.inner); genes(model) .* "#gecko"]
 
 """
-    n_metabolites(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 Return the number of metabolites, both real and pseudo, for a [`GeckoModel`](@ref).
 """
diff --git a/src/base/types/wrappers/SMomentModel.jl b/src/base/types/wrappers/SMomentModel.jl
index bdf50a71d..98f8041f7 100644
--- a/src/base/types/wrappers/SMomentModel.jl
+++ b/src/base/types/wrappers/SMomentModel.jl
@@ -1,8 +1,11 @@
 
 """
-    struct _smoment_column
+$(TYPEDEF)
 
 A helper type that describes the contents of [`SMomentModel`](@ref)s.
+
+# Fields
+$(TYPEDFIELDS)
 """
 struct _smoment_column
     reaction_idx::Int # number of the corresponding reaction in the inner model
@@ -13,7 +16,7 @@ struct _smoment_column
 end
 
 """
-    struct SMomentModel <: ModelWrapper
+$(TYPEDEF)
 
 An enzyme-capacity-constrained model using sMOMENT algorithm, as described by
 *Bekiaris, Pavlos Stephanos, and Steffen Klamt, "Automatic construction of
@@ -47,6 +50,9 @@ This implementation allows easy access to fluxes from the split reactions
 (available in `reactions(model)`), while the original "simple" reactions from
 the wrapped model are retained as [`fluxes`](@ref). All additional constraints
 are implemented using [`coupling`](@ref) and [`coupling_bounds`](@ref).
+
+# Fields
+$(TYPEDFIELDS)
 """
 struct SMomentModel <: ModelWrapper
     columns::Vector{_smoment_column}
@@ -58,7 +64,7 @@ end
 unwrap_model(model::SMomentModel) = model.inner
 
 """
-    stoichiometry(model::SMomentModel)
+$(TYPEDSIGNATURES)
 
 Return a stoichiometry of the [`SMomentModel`](@ref). The enzymatic reactions
 are split into unidirectional forward and reverse ones.
@@ -67,14 +73,14 @@ stoichiometry(model::SMomentModel) =
     stoichiometry(model.inner) * _smoment_column_reactions(model)
 
 """
-    objective(model::SMomentModel)
+$(TYPEDSIGNATURES)
 
 Reconstruct an objective of the [`SMomentModel`](@ref).
 """
 objective(model::SMomentModel) = _smoment_column_reactions(model)' * objective(model.inner)
 
 """
-    reactions(model::SMomentModel)
+$(TYPEDSIGNATURES)
 
 Returns the internal reactions in a [`SMomentModel`](@ref) (these may be split
 to forward- and reverse-only parts; reactions IDs are mangled accordingly with
@@ -89,14 +95,14 @@ reactions(model::SMomentModel) =
     end
 
 """
-    n_reactions(model::SMomentModel)
+$(TYPEDSIGNATURES)
 
 The number of reactions (including split ones) in [`SMomentModel`](@ref).
 """
 n_reactions(model::SMomentModel) = length(model.columns)
 
 """
-    bounds(model::SMomentModel)
+$(TYPEDSIGNATURES)
 
 Return the variable bounds for [`SMomentModel`](@ref).
 """
@@ -104,7 +110,7 @@ bounds(model::SMomentModel) =
     ([col.lb for col in model.columns], [col.ub for col in model.columns])
 
 """
-    reaction_flux(model::SMomentModel)
+$(TYPEDSIGNATURES)
 
 Get the mapping of the reaction rates in [`SMomentModel`](@ref) to the original
 fluxes in the wrapped model.
@@ -113,7 +119,7 @@ reaction_flux(model::SMomentModel) =
     _smoment_column_reactions(model)' * reaction_flux(model.inner)
 
 """
-    coupling(model::SMomentModel)
+$(TYPEDSIGNATURES)
 
 Return the coupling of [`SMomentModel`](@ref). That combines the coupling of
 the wrapped model, coupling for split reactions, and the coupling for the total
@@ -125,7 +131,7 @@ coupling(model::SMomentModel) = vcat(
 )
 
 """
-    n_coupling_constraints(model::SMomentModel)
+$(TYPEDSIGNATURES)
 
 Count the coupling constraints in [`SMomentModel`](@ref) (refer to
 [`coupling`](@ref) for details).
@@ -133,7 +139,7 @@ Count the coupling constraints in [`SMomentModel`](@ref) (refer to
 n_coupling_constraints(model::SMomentModel) = n_coupling_constraints(model.inner) + 1
 
 """
-    coupling_bounds(model::SMomentModel)
+$(TYPEDSIGNATURES)
 
 The coupling bounds for [`SMomentModel`](@ref) (refer to [`coupling`](@ref) for
 details).
diff --git a/src/base/utils/Annotation.jl b/src/base/utils/Annotation.jl
index 6ec3636aa..9832d0d4c 100644
--- a/src/base/utils/Annotation.jl
+++ b/src/base/utils/Annotation.jl
@@ -4,10 +4,7 @@ _annotations(r::Reaction) = r.annotations
 _annotations(g::Gene) = g.annotations
 
 """
-    annotation_index(
-        xs::AbstractDict{String};
-        annotations = _annotations,
-    )::Dict{String,Dict{String,[String]}}
+$(TYPEDSIGNATURES)
 
 Extract annotations from a dictionary of items `xs` and build an index that
 maps annotation "kinds" (e.g. `"PubChem"`) to the mapping from the annotations
@@ -42,9 +39,7 @@ function annotation_index(
 end
 
 """
-    ambiguously_identified_items(
-        index::Dict{String,Dict{String,[String]}},
-    )::Vector{String}
+$(TYPEDSIGNATURES)
 
 Find items (genes, metabolites, ...) from the annotation index that are
 identified non-uniquely by at least one of their annotations.
diff --git a/src/base/utils/Reaction.jl b/src/base/utils/Reaction.jl
index 8661d8658..011b732b9 100644
--- a/src/base/utils/Reaction.jl
+++ b/src/base/utils/Reaction.jl
@@ -1,5 +1,5 @@
 """
-    check_duplicate_reaction(rxn::Reaction, rxns::Dict{String, Reaction}; only_metabolites=true)
+$(TYPEDSIGNATURES)
 
 Check if `rxn` already exists in `rxns` but has another `id`.
 If `only_metabolites` is `true` then only the metabolite `id`s are checked.
@@ -39,7 +39,7 @@ function check_duplicate_reaction(
 end
 
 """
-    is_boundary(rxn_dict::Dict{String, Float64})
+$(TYPEDSIGNATURES)
 
 Return true if the reaction denoted by `rxn_dict` is a boundary reaction, otherwise return false.
 Checks if on boundary by inspecting the number of metabolites in `rxn_dict`.
@@ -57,7 +57,7 @@ is_boundary(rxn::Reaction) = is_boundary(rxn.metabolites)
 is_boundary(model::StandardModel, rxn::Reaction) = is_boundary(rxn) # for consistency with functions below
 
 """
-    reaction_atom_balance(model::StandardModel, rxn)
+$(TYPEDSIGNATURES)
 
 Returns a dictionary mapping the stoichiometry of atoms through a single reaction. Uses the
 metabolite information in `model` to determine the mass balance. Accepts a reaction
@@ -87,7 +87,7 @@ reaction_atom_balance(model::StandardModel, rxn::Reaction) =
     reaction_atom_balance(model, rxn.id)
 
 """
-    reaction_mass_balanced(model::StandardModel, rxn)
+$(TYPEDSIGNATURES)
 
 Checks if `rxn` is atom balanced. Returns a boolean for whether the reaction is balanced,
 and the associated balance of atoms for convenience (useful if not balanced). Calls
@@ -105,7 +105,7 @@ reaction_mass_balanced(model::StandardModel, reaction_dict::Dict{String,Float64}
     all(values(reaction_atom_balance(model, reaction_dict)) .== 0)
 
 """
-    stoichiometry_string(rxn_dict::Dict{String, Float64}; format_id = x -> x)
+$(TYPEDSIGNATURES)
 
 Return the reaction equation as a string. The metabolite strings can be manipulated by
 setting `format_id`.
@@ -130,7 +130,7 @@ function stoichiometry_string(req; format_id = x -> x)
 end
 
 """
-    stoichiometry_string(rxn::Reaction; kwargs)
+$(TYPEDSIGNATURES)
 
 Alternative of [`stoichiometry_string`](@ref) take takes a `Reaction` as an argument.
 """
diff --git a/src/base/utils/Serialized.jl b/src/base/utils/Serialized.jl
index 2ed27e51e..577d101c4 100644
--- a/src/base/utils/Serialized.jl
+++ b/src/base/utils/Serialized.jl
@@ -1,6 +1,6 @@
 
 """
-    serialize_model(model::MM, filename::String)::Serialized{MM} where {MM<:MetabolicModel}
+$(TYPEDSIGNATURES)
 
 Serialize the `model` to file `filename`, returning a [`Serialized`](@ref)
 model that can be loaded back transparently by [`precache!`](@ref). The result
@@ -22,7 +22,7 @@ function serialize_model(
 end
 
 """
-    serialize_model(model::Serialized, filename::String)::Serialized
+$(TYPEDSIGNATURES)
 
 Specialization of [`serialize_model`](@ref) that prevents nested serialization
 of already-serialized models.
diff --git a/src/base/utils/StandardModel.jl b/src/base/utils/StandardModel.jl
index 83d6be64d..3da400441 100644
--- a/src/base/utils/StandardModel.jl
+++ b/src/base/utils/StandardModel.jl
@@ -1,6 +1,6 @@
 
 """
-    Base.copy(m::StandardModel)
+$(TYPEDSIGNATURES)
 
 Shallow copy of a [`StandardModel`](@ref)
 """
@@ -12,7 +12,7 @@ Base.copy(m::StandardModel) = StandardModel(
 )
 
 """
-    Base.copy(r::Reaction)
+$(TYPEDSIGNATURES)
 
 Shallow copy of a [`Reaction`](@ref)
 """
@@ -29,7 +29,7 @@ Base.copy(r::Reaction) = Reaction(
 )
 
 """
-    Base.copy(m::Metabolite)
+$(TYPEDSIGNATURES)
 
 Shallow copy of a [`Metabolite`](@ref)
 """
@@ -43,7 +43,7 @@ Base.copy(m::Metabolite) = Metabolite(
 )
 
 """
-    Base.copy(g::Gene)
+$(TYPEDSIGNATURES)
 
 Shallow copy of a [`Gene`](@ref)
 """
diff --git a/src/base/utils/bounds.jl b/src/base/utils/bounds.jl
index 763457646..5ca724795 100644
--- a/src/base/utils/bounds.jl
+++ b/src/base/utils/bounds.jl
@@ -1,5 +1,5 @@
 """
-    gamma_bounds(gamma)
+$(TYPEDSIGNATURES)
 
 A bounds-generating function for [`flux_variability_analysis`](@ref) that
 limits the objective value to be at least `gamma*Z₀`, as usual in COBRA
@@ -11,7 +11,7 @@ flux_variability_analysis(model, some_optimizer; bounds = gamma_bounds(0.9))
 gamma_bounds(gamma) = z -> (gamma * z, Inf)
 
 """
-    objective_bounds(tolerance)
+$(TYPEDSIGNATURES)
 
 A bounds-generating function for [`flux_variability_analysis`](@ref) that
 limits the objective value to a small multiple of Z₀. Use as `bounds` argument,
diff --git a/src/base/utils/chemical_formulas.jl b/src/base/utils/chemical_formulas.jl
index da55f43fa..d4c081619 100644
--- a/src/base/utils/chemical_formulas.jl
+++ b/src/base/utils/chemical_formulas.jl
@@ -1,6 +1,6 @@
 
 """
-    _parse_formula(f::String)::MetaboliteFormula
+$(TYPEDSIGNATURES)
 
 Parse a formula in format `C2H6O` into a [`MetaboliteFormula`](@ref), which is
 basically a dictionary of atom counts in the molecule.
@@ -17,7 +17,7 @@ function _parse_formula(f::String)::MetaboliteFormula
 end
 
 """
-    _unparse_formula(f::MetaboliteFormula)::String
+$(TYPEDSIGNATURES)
 
 Format [`MetaboliteFormula`](@ref) to `String`.
 """
diff --git a/src/base/utils/enzymes.jl b/src/base/utils/enzymes.jl
index 152437fb1..cf86b71b7 100644
--- a/src/base/utils/enzymes.jl
+++ b/src/base/utils/enzymes.jl
@@ -1,5 +1,5 @@
 """
-    gene_product_dict(model::GeckoModel, opt_model)
+$(TYPEDSIGNATURES)
 
 Return a dictionary mapping protein molar concentrations to their ids. The
 argument `opt_model` is a solved optimization problem, typically returned by
@@ -11,14 +11,14 @@ gene_product_dict(model::GeckoModel, opt_model) =
     Dict(genes(model) .=> value.(opt_model[:x])[(length(model.columns)+1):end]) : nothing
 
 """
-    gene_product_dict(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 A pipe-able variant of [`gene_product_dict`](@ref).
 """
 gene_product_dict(model::GeckoModel) = x -> gene_product_dict(model, x)
 
 """
-    gene_product_mass_group_dict(model::GeckoModel, opt_model)
+$(TYPEDSIGNATURES)
 
 Extract the mass utilization in mass groups from a solved [`GeckoModel`](@ref).
 """
@@ -32,7 +32,7 @@ gene_product_mass_group_dict(model::GeckoModel, opt_model) =
     ) : nothing
 
 """
-    gene_product_mass_group_dict(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 A pipe-able variant of [`gene_product_mass_group_dict`](@ref).
 """
@@ -40,7 +40,7 @@ gene_product_mass_group_dict(model::GeckoModel) =
     x -> gene_product_mass_group_dict(model, x)
 
 """
-    gene_product_mass(model::SMomentModel)
+$(TYPEDSIGNATURES)
 
 Extract the total mass utilization in a solved [`SMomentModel`](@ref).
 """
@@ -49,8 +49,7 @@ gene_product_mass(model::SMomentModel, opt_model) =
     sum((col.capacity_required for col in model.columns) .* value.(opt_model[:x])) : nothing
 
 """
-    gene_product_mass(model::SMomentModel)
-
+$(TYPEDSIGNATURES)
 
 A pipe-able variant of [`gene_product_mass`](@ref).
 """
diff --git a/src/base/utils/fluxes.jl b/src/base/utils/fluxes.jl
index 6b9deecd8..cf23a3f9d 100644
--- a/src/base/utils/fluxes.jl
+++ b/src/base/utils/fluxes.jl
@@ -1,5 +1,5 @@
 """
-    metabolite_fluxes(model::MetabolicModel, flux_dict::Dict{String, Float64})
+$(TYPEDSIGNATURES)
 
 Return two dictionaries of metabolite `id`s mapped to reactions that consume or
 produce them, given the flux distribution supplied in `flux_dict`.
@@ -33,7 +33,7 @@ function metabolite_fluxes(model::MetabolicModel, flux_dict::Dict{String,Float64
 end
 
 """
-    atom_fluxes(model::MetabolicModel, reaction_fluxes::Dict{String, Float64})
+$(TYPEDSIGNATURES)
 
 Return a dictionary mapping the flux of atoms across a flux solution given by
 `reaction_fluxes` using the reactions in `model` to determine the appropriate stoichiometry.
diff --git a/src/base/utils/gecko.jl b/src/base/utils/gecko.jl
index e25ec2863..219f45b6a 100644
--- a/src/base/utils/gecko.jl
+++ b/src/base/utils/gecko.jl
@@ -1,6 +1,6 @@
 
 """
-    _gecko_reaction_name(original_name::String, direction::Int)
+$(TYPEDSIGNATURES)
 
 Internal helper for systematically naming reactions in [`GeckoModel`](@ref).
 """
@@ -10,7 +10,7 @@ _gecko_reaction_name(original_name::String, direction::Int, isozyme_idx::Int) =
     "$original_name#reverse#$isozyme_idx"
 
 """
-    _gecko_reaction_column_reactions(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 Retrieve a utility mapping between reactions and split reactions; rows
 correspond to "original" reactions, columns correspond to "split" reactions.
@@ -19,7 +19,7 @@ _gecko_reaction_column_reactions(model::GeckoModel) =
     _gecko_reaction_column_reactions(model.columns, model.inner)
 
 """
-    _gecko_reaction_column_reactions(columns, inner)
+$(TYPEDSIGNATURES)
 
 Helper method that doesn't require the whole [`GeckoModel`](@ref).
 """
@@ -32,7 +32,7 @@ _gecko_reaction_column_reactions(columns, inner) = sparse(
 )
 
 """
-    _gecko_reaction_coupling(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 Compute the part of the coupling for [`GeckoModel`](@ref) that limits the
 "arm" reactions (which group the individual split unidirectional reactions).
@@ -52,7 +52,7 @@ _gecko_reaction_coupling(model::GeckoModel) =
     end
 
 """
-    _gecko_gene_product_coupling(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 Compute the part of the coupling for GeckoModel that limits the amount of each
 kind of protein available.
@@ -73,7 +73,7 @@ _gecko_gene_product_coupling(model::GeckoModel) =
     end
 
 """
-    _gecko_mass_group_coupling(model::GeckoModel)
+$(TYPEDSIGNATURES)
 
 Compute the part of the coupling for [`GeckoModel`](@ref) that limits the total
 mass of each group of gene products.
diff --git a/src/base/utils/gene_associations.jl b/src/base/utils/gene_associations.jl
index 9e78a5999..109317900 100644
--- a/src/base/utils/gene_associations.jl
+++ b/src/base/utils/gene_associations.jl
@@ -1,6 +1,6 @@
 
 """
-    _parse_grr(gpa::SBML.GeneProductAssociation)::GeneAssociation
+$(TYPEDSIGNATURES)
 
 Parse `SBML.GeneProductAssociation` structure to the simpler GeneAssociation.
 The input must be (implicitly) in a positive DNF.
@@ -20,10 +20,7 @@ function _parse_grr(gpa::SBML.GeneProductAssociation)::GeneAssociation
 end
 
 """
-    _unparse_grr(
-        ::Type{SBML.GeneProductAssociation},
-        x::GeneAssociation,
-    )::SBML.GeneAssociation
+$(TYPEDSIGNATURES)
 
 Convert a GeneAssociation to the corresponding `SBML.jl` structure.
 """
@@ -35,7 +32,7 @@ function _unparse_grr(
 end
 
 """
-    _parse_grr(s::String)::GeneAssociation
+$(TYPEDSIGNATURES)
 
 Parse a DNF gene association rule in format `(YIL010W and YLR043C) or (YIL010W
 and YGR209C)` to `GeneAssociation. Also accepts `OR`, `|`, `||`, `AND`, `&`,
@@ -52,7 +49,7 @@ julia> _parse_grr("(YIL010W and YLR043C) or (YIL010W and YGR209C)")
 _parse_grr(s::String)::Maybe{GeneAssociation} = _maybemap(_parse_grr, _parse_grr_to_sbml(s))
 
 """
-    _parse_grr_to_sbml(str::String)::Maybe{SBML.GeneProductAssociation}
+$(TYPEDSIGNATURES)
 
 Internal helper for parsing the string GRRs into SBML data structures. More
 general than [`_parse_grr`](@ref).
@@ -119,7 +116,7 @@ function _parse_grr_to_sbml(str::String)::Maybe{SBML.GeneProductAssociation}
 end
 
 """
-    unparse_grr(grr::Vector{Vector{Gene}}
+$(TYPEDSIGNATURES)
 
 Converts a nested string gene reaction array  back into a gene reaction rule
 string.
diff --git a/src/base/utils/guesskey.jl b/src/base/utils/guesskey.jl
index 4a9ad6302..183e82656 100644
--- a/src/base/utils/guesskey.jl
+++ b/src/base/utils/guesskey.jl
@@ -1,6 +1,6 @@
 
 """
-    _guesskey(ks, possibilities)
+$(TYPEDSIGNATURES)
 
 Unfortunately, many model types that contain dictionares do not have
 standardized field names, so we need to try a few possibilities and guess the
@@ -22,7 +22,7 @@ function _guesskey(avail, possibilities)
 end
 
 """
-    gets(collection, fail, keys)
+$(TYPEDSIGNATURES)
 
 Return `fail` if key in `keys` is not in `collection`, otherwise
 return `collection[key]`. Useful if may different keys need to be
diff --git a/src/base/utils/looks_like.jl b/src/base/utils/looks_like.jl
index 1b29f23f8..69f2d93d9 100644
--- a/src/base/utils/looks_like.jl
+++ b/src/base/utils/looks_like.jl
@@ -1,9 +1,5 @@
 """
-    looks_like_exchange_reaction(rxn_id::String;
-        exclude_biomass = false,
-        biomass_strings = _constants.biomass_strings,
-        exchange_prefixes = _constants.exchange_prefixes,
-    )
+$(TYPEDSIGNATURES)
 
 A predicate that matches reaction identifiers that look like
 exchange or biomass reactions, given the usual naming schemes in common model
@@ -13,6 +9,12 @@ occurences of `biomass_strings` in the reaction id.
 
 Also see [`find_exchange_reactions`](@ref).
 
+# Note
+While `looks_like_exchange_reaction` is useful for heuristically finding a
+reaction, it is preferable to use standardized terms for finding reactions (e.g.
+SBO terms). See [`is_exchange_reaction`](@ref) for a more systematic
+alternative.
+
 # Example
 ```
 findall(looks_like_exchange_reaction, reactions(model)) # returns indices
@@ -35,7 +37,7 @@ function looks_like_exchange_reaction(
 end
 
 """
-    find_exchange_reactions(m::MetabolicModel; kwargs...)
+$(TYPEDSIGNATURES)
 
 Shortcut for finding exchange reaction indexes in a model; arguments are
 forwarded to [`looks_like_exchange_reaction`](@ref).
@@ -44,7 +46,7 @@ find_exchange_reactions(m::MetabolicModel; kwargs...) =
     findall(id -> looks_like_exchange_reaction(id; kwargs...), reactions(m))
 
 """
-    find_exchange_reaction_ids(m::MetabolicModel; kwargs...)
+$(TYPEDSIGNATURES)
 
 Shortcut for finding exchange reaction identifiers in a model; arguments are
 forwarded to [`looks_like_exchange_reaction`](@ref).
@@ -53,17 +55,19 @@ find_exchange_reaction_ids(m::MetabolicModel; kwargs...) =
     filter(id -> looks_like_exchange_reaction(id, kwargs...), reactions(m))
 
 """
-    looks_like_biomass_reaction(rxn_id::String;
-        exclude_exchanges = false,
-        exchange_prefixes = _constants.exchange_prefixes,
-        biomass_strings = _constants.biomass_strings,
-    )::Bool
+$(TYPEDSIGNATURES)
 
 A predicate that matches reaction identifiers that look like biomass reactions.
 Biomass reactions are identified by looking for occurences of `biomass_strings`
 in the reaction id. If `exclude_exchanges` is set, the strings that look like
 exchanges (from [`looks_like_exchange_reaction`](@ref)) will not match.
 
+# Note
+While `looks_like_biomass_reaction` is useful for heuristically finding a
+reaction, it is preferable to use standardized terms for finding reactions (e.g.
+SBO terms). See [`is_biomass_reaction`](@ref) for a more systematic
+alternative.
+
 # Example
 ```
 filter(looks_like_biomass_reaction, reactions(model)) # returns strings
@@ -81,7 +85,7 @@ function looks_like_biomass_reaction(
 end
 
 """
-    find_biomass_reactions(m::MetabolicModel; kwargs...)
+$(TYPEDSIGNATURES)
 
 Shortcut for finding biomass reaction indexes in a model; arguments are
 forwarded to [`looks_like_biomass_reaction`](@ref).
@@ -90,7 +94,7 @@ find_biomass_reactions(m::MetabolicModel; kwargs...) =
     findall(id -> looks_like_biomass_reaction(id; kwargs...), reactions(m))
 
 """
-    find_biomass_reaction_ids(m::MetabolicModel; kwargs...)
+$(TYPEDSIGNATURES)
 
 Shortcut for finding biomass reaction identifiers in a model; arguments are
 forwarded to [`looks_like_biomass_reaction`](@ref).
@@ -99,9 +103,7 @@ find_biomass_reaction_ids(m::MetabolicModel; kwargs...) =
     filter(id -> looks_like_biomass_reaction(id; kwargs...), reactions(m))
 
 """
-    looks_like_extracellular_metabolite(rxn_id::String;
-        extracellular_suffixes = _constants.extracellular_suffixes,
-    )::Bool
+$(TYPEDSIGNATURES)
 
 A predicate that matches metabolite identifiers that look like they are extracellular
 metabolites. Extracellular metabolites are identified by `extracellular_suffixes` at the end of the
@@ -121,18 +123,27 @@ function looks_like_extracellular_metabolite(
 end
 
 """
-    find_extracellular_metabolites(m::MetabolicModel; kwargs...)
+$(TYPEDSIGNATURES)
 
 Shortcut for finding extracellular metabolite indexes in a model; arguments are
 forwarded to [`looks_like_extracellular_metabolite`](@ref).
 """
 find_extracellular_metabolites(m::MetabolicModel; kwargs...) =
     findall(id -> looks_like_extracellular_metabolite(id; kwargs...), metabolites(m))
+
 """
-    find_extracellular_metabolite_ids(m::MetabolicModel; kwargs...)
+$(TYPEDSIGNATURES)
 
 Shortcut for finding extracellular metabolite identifiers in a model; arguments are
 forwarded to [`looks_like_extracellular_metabolite`](@ref).
 """
 find_extracellular_metabolite_ids(m::MetabolicModel; kwargs...) =
     findall(id -> looks_like_extracellular_metabolite(id; kwargs...), metabolites(m))
+
+@_is_reaction_fn "exchange" Identifiers.EXCHANGE_REACTIONS
+@_is_reaction_fn "transport" Identifiers.TRANSPORT_REACTIONS
+@_is_reaction_fn "biomass" Identifiers.BIOMASS_REACTIONS
+@_is_reaction_fn "atp_maintenance" Identifiers.ATP_MAINTENANCE_REACTIONS
+@_is_reaction_fn "pseudo" Identifiers.PSEUDOREACTIONS
+@_is_reaction_fn "metabolic" Identifiers.METABOLIC_REACTIONS
+@_is_reaction_fn "spontaneous" Identifiers.SPONTANEOUS_REACTIONS
diff --git a/src/base/utils/smoment.jl b/src/base/utils/smoment.jl
index 15751715c..4bbf4783c 100644
--- a/src/base/utils/smoment.jl
+++ b/src/base/utils/smoment.jl
@@ -1,6 +1,6 @@
 
 """
-    _smoment_reaction_name(original_name::String, direction::Int)
+$(TYPEDSIGNATURES)
 
 Internal helper for systematically naming reactions in [`SMomentModel`](@ref).
 """
@@ -9,7 +9,7 @@ _smoment_reaction_name(original_name::String, direction::Int) =
     direction > 0 ? "$original_name#forward" : "$original_name#reverse"
 
 """
-    _smoment_column_reactions(model::SMomentModel)
+$(TYPEDSIGNATURES)
 
 Retrieve a utility mapping between reactions and split reactions; rows
 correspond to "original" reactions, columns correspond to "split" reactions.
@@ -23,7 +23,7 @@ _smoment_column_reactions(model::SMomentModel) = sparse(
 )
 
 """
-    smoment_isozyme_speed(isozyme::Isozyme, gene_product_molar_mass)
+$(TYPEDSIGNATURES)
 
 Compute a "score" for picking the most viable isozyme for
 [`make_smoment_model`](@ref), based on maximum kcat divided by relative mass of
@@ -37,7 +37,7 @@ smoment_isozyme_speed(isozyme::Isozyme, gene_product_molar_mass) =
     )
 
 """
-    smoment_isozyme_speed(gene_product_molar_mass::Function)
+$(TYPEDSIGNATURES)
 
 A piping- and argmax-friendly overload of [`smoment_isozyme_speed`](@ref).
 
diff --git a/src/io/h5.jl b/src/io/h5.jl
index 0bc7b66e5..5d7785305 100644
--- a/src/io/h5.jl
+++ b/src/io/h5.jl
@@ -1,6 +1,6 @@
 
 """
-    load_h5_model(file_name::String)::HDF5Model
+$(TYPEDSIGNATURES)
 
 Return a HDF5Model associated with the given file. Does not actually load
 anything (for efficiency) -- use [`precache!`](@ref) to start pulling data into
@@ -11,7 +11,7 @@ function load_h5_model(file_name::String)::HDF5Model
 end
 
 """
-    save_h5_model(model::MetabolicModel, file_name::String)::HDF5Model
+$(TYPEDSIGNATURES)
 
 Converts and writes a metabolic model to disk in the HDF5 format.
 
@@ -41,7 +41,7 @@ function save_h5_model(model::MetabolicModel, file_name::String)::HDF5Model
 end
 
 """
-    Base.close(model::HDF5Model)
+$(TYPEDSIGNATURES)
 
 Close (and un-cache) the [`HDF5Model`](@ref) data. This allows the associated
 file to be opened for writing again.
diff --git a/src/io/io.jl b/src/io/io.jl
index 53f2e1347..4c8b268ba 100644
--- a/src/io/io.jl
+++ b/src/io/io.jl
@@ -1,6 +1,6 @@
 
 """
-    load_model(file_name::String)::MetabolicModel
+$(TYPEDSIGNATURES)
 
 Generic function for loading models that chooses a specific loader function
 from the `file_name` extension, or throws an error.
@@ -28,7 +28,7 @@ end
 
 
 """
-    load_model(type::Type{T}, file_name::String)::T where T
+$(TYPEDSIGNATURES)
 
 Helper function tht loads the model using [`load_model`](@ref) and return it
 converted to `type`.
@@ -42,7 +42,7 @@ function load_model(type::Type{T}, file_name::String)::T where {T<:MetabolicMode
 end
 
 """
-    save_model(model::MetabolicModel, file_name::String)
+$(TYPEDSIGNATURES)
 
 Generic function for saving models that chooses a specific writer function
 from the `file_name` extension, or throws an error.
diff --git a/src/io/json.jl b/src/io/json.jl
index 6186ab5bb..918267a5c 100644
--- a/src/io/json.jl
+++ b/src/io/json.jl
@@ -1,5 +1,5 @@
 """
-    load_json_model(filename::String)::JSONModel
+$(TYPEDSIGNATURES)
 
 Load and return a JSON-formatted model that is stored in `file_name`.
 """
@@ -8,7 +8,7 @@ function load_json_model(filename::String)::JSONModel
 end
 
 """
-    save_json_model(model::MetabolicModel, file_name::String)
+$(TYPEDSIGNATURES)
 
 Save a [`JSONModel`](@ref) in `model` to a JSON file `file_name`.
 
diff --git a/src/io/mat.jl b/src/io/mat.jl
index 314238b93..84b205868 100644
--- a/src/io/mat.jl
+++ b/src/io/mat.jl
@@ -1,6 +1,6 @@
 
 """
-    load_mat_model(file_name::String)
+$(TYPEDSIGNATURES)
 
 Load and return a MATLAB file `file_name` that contains a COBRA-compatible
 model.
@@ -12,7 +12,7 @@ function load_mat_model(file_name::String)::MATModel
 end
 
 """
-    save_mat_model(model::MetabolicModel, file_name::String; model_name::String="model")
+$(TYPEDSIGNATURES)
 
 Save a [`MATModel`](@ref) in `model` to a MATLAB file `file_name` in a format
 compatible with other MATLAB-based COBRA software.
diff --git a/src/io/sbml.jl b/src/io/sbml.jl
index 9debac4e3..574a60300 100644
--- a/src/io/sbml.jl
+++ b/src/io/sbml.jl
@@ -1,6 +1,6 @@
 
 """
-    load_sbml_model(file_name::String)::SBMLModel
+$(TYPEDSIGNATURES)
 
 Load and return a SBML XML model in `file_name`.
 """
@@ -9,7 +9,7 @@ function load_sbml_model(file_name::String)::SBMLModel
 end
 
 """
-    write_sbml_model(model::MetabolicModel, file_name::String)
+$(TYPEDSIGNATURES)
 
 Write a given SBML model to `file_name`.
 """
diff --git a/src/io/show/MetabolicModel.jl b/src/io/show/MetabolicModel.jl
index c9988b590..b600208be 100644
--- a/src/io/show/MetabolicModel.jl
+++ b/src/io/show/MetabolicModel.jl
@@ -1,4 +1,6 @@
 """
+$(TYPEDSIGNATURES)
+
 Pretty printing of everything metabolic-modelish.
 """
 function Base.show(io::IO, ::MIME"text/plain", m::MetabolicModel)
diff --git a/src/io/show/Reaction.jl b/src/io/show/Reaction.jl
index b82c08329..b5e340615 100644
--- a/src/io/show/Reaction.jl
+++ b/src/io/show/Reaction.jl
@@ -1,6 +1,6 @@
 
 """
-    _pretty_substances(ss::Vector{String})::String
+$(TYPEDSIGNATURES)
 
 Nicely format a substance list.
 """
diff --git a/src/io/show/Serialized.jl b/src/io/show/Serialized.jl
index 83e427b93..598e2ceff 100644
--- a/src/io/show/Serialized.jl
+++ b/src/io/show/Serialized.jl
@@ -1,6 +1,6 @@
 
 """
-    Base.show(io::IO, ::MIME"text/plain", m::Serialized{M}) where {M}
+$(TYPEDSIGNATURES)
 
 Show the [`Serialized`](@ref) model without unnecessarily loading it.
 """
diff --git a/src/io/show/pretty_printing.jl b/src/io/show/pretty_printing.jl
index 8eb258345..8df6f6462 100644
--- a/src/io/show/pretty_printing.jl
+++ b/src/io/show/pretty_printing.jl
@@ -1,5 +1,5 @@
 """
-    _pretty_print_keyvals(io, def::String, payload; kwargs...)
+$(TYPEDSIGNATURES)
 
 Nicely prints keys and values.
 """
@@ -7,11 +7,7 @@ _pretty_print_keyvals(io, def::String, payload; kwargs...) =
     _pretty_print_keyvals(io, def, isnothing(payload) ? "---" : string(payload); kwargs...)
 
 """
-    _pretty_print_keyvals(
-        io,
-        def::String,
-        payload::String
-    )
+$(TYPEDSIGNATURES)
 
 Specialization of `_pretty_print_keyvals` for plain strings.
 """
@@ -25,11 +21,7 @@ function _pretty_print_keyvals(io, def::String, payload::String)
 end
 
 """
-    _pretty_print_keyvals(
-        io,
-        def::String,
-        payload::Dict
-    )
+$(TYPEDSIGNATURES)
 
 Specialization of `_pretty_print_keyvals` for dictionaries.
 """
diff --git a/src/reconstruction/CoreModel.jl b/src/reconstruction/CoreModel.jl
index 1336b8fc4..e791cce3e 100644
--- a/src/reconstruction/CoreModel.jl
+++ b/src/reconstruction/CoreModel.jl
@@ -1,5 +1,5 @@
 """
-    add_reactions!(model::CoreModel, rxns::Vector{Reaction})
+$(TYPEDSIGNATURES)
 
 Add `rxns` to `model` efficiently. The model must already contain the metabolites used by
 `rxns` in the model.
@@ -32,7 +32,7 @@ function add_reactions!(model::CoreModel, rxns::Vector{Reaction})
 end
 
 """
-    add_reaction!(model::CoreModel, rxn::Reaction)
+$(TYPEDSIGNATURES)
 
 Add `rxn` to `model`. The model must already contain the metabolites used by
 `rxn` in the model.
@@ -40,15 +40,7 @@ Add `rxn` to `model`. The model must already contain the metabolites used by
 add_reaction!(model::CoreModel, rxn::Reaction) = add_reactions!(model, [rxn])
 
 """
-    add_reactions(
-        m::CoreModel,
-        s::VecType,
-        b::VecType,
-        c::AbstractFloat,
-        xl::AbstractFloat,
-        xu::AbstractFloat;
-        check_consistency = false,
-    )
+$(TYPEDSIGNATURES)
 
 Add reaction(s) to a `CoreModel` model `m`.
 """
@@ -73,17 +65,7 @@ function add_reactions(
 end
 
 """
-    add_reactions(
-        m::CoreModel,
-        s::VecType,
-        b::VecType,
-        c::AbstractFloat,
-        xl::AbstractFloat,
-        xu::AbstractFloat,
-        rxn::String,
-        mets::K;
-        check_consistency = false,
-    )
+$(TYPEDSIGNATURES)
 """
 function add_reactions(
     m::CoreModel,
@@ -110,15 +92,7 @@ function add_reactions(
 end
 
 """
-    add_reactions(
-        m::CoreModel,
-        Sp::MatType,
-        b::VecType,
-        c::VecType,
-        xl::VecType,
-        xu::VecType;
-        check_consistency = false,
-    )
+$(TYPEDSIGNATURES)
 """
 function add_reactions(
     m::CoreModel,
@@ -145,7 +119,7 @@ function add_reactions(
 end
 
 """
-    add_reactions(m1::CoreModel, m2::CoreModel; check_consistency = false)
+$(TYPEDSIGNATURES)
 
 Add all reactions from `m2` to `m1`.
 """
@@ -164,17 +138,7 @@ function add_reactions(m1::CoreModel, m2::CoreModel; check_consistency = false)
 end
 
 """
-    add_reactions(
-        m::CoreModel,
-        Sp::MatType,
-        b::VecType,
-        c::VecType,
-        xl::VecType,
-        xu::VecType,
-        rxns::StringVecType,
-        mets::StringVecType;
-        check_consistency = false,
-    )
+$(TYPEDSIGNATURES)
 """
 function add_reactions(
     m::CoreModel,
@@ -245,18 +209,7 @@ function add_reactions(
 end
 
 """
-    verify_consistency(
-        m::CoreModel,
-        Sp::M,
-        b::V,
-        c::V,
-        xl::B,
-        xu::B,
-        names::K,
-        mets::K,
-        new_reactions,
-        new_metabolites,
-    ) where {M<:MatType,V<:VecType,B<:VecTypeK<:StringVecType}
+$(TYPEDSIGNATURES)
 
 Check the consistency of given reactions with existing reactions in `m`.
 
@@ -438,11 +391,7 @@ end
 end
 
 """
-    change_objective!(
-        model::CoreModel,
-        rxn_idxs::Vector{Int};
-        weights = ones(length(rxn_idxs)),
-    )
+$(TYPEDSIGNATURES)
 
 Change the objective to reactions at given indexes, optionally specifying their
 `weights` in the same order. By default, all set weights are 1.
@@ -458,7 +407,7 @@ function change_objective!(
 end
 
 """
-    change_objective!(model::CoreModel, rxn_idx::Int)
+$(TYPEDSIGNATURES)
 
 Change objective function of a CoreModel to a single `1` at reaction index
 `rxn_idx`.
@@ -466,11 +415,7 @@ Change objective function of a CoreModel to a single `1` at reaction index
 change_objective!(model::CoreModel, rxn_idx::Int) = change_objective!(model, [rxn_idx])
 
 """
-    change_objective!(
-        model::CoreModel,
-        rxn_ids::Vector{String};
-        weights = ones(length(rxn_ids)),
-    )
+$(TYPEDSIGNATURES)
 
 Change objective of given reaction IDs, optionally specifying objective
 `weights` in the same order as `rxn_ids`. By default, all set weights are 1.
@@ -487,7 +432,7 @@ function change_objective!(
 end
 
 """
-    change_objective!(model::CoreModel, rxn_id::String)
+$(TYPEDSIGNATURES)
 
 Change objective function of a CoreModel to a single `1` at the given reaction
 ID.
diff --git a/src/reconstruction/CoreModelCoupled.jl b/src/reconstruction/CoreModelCoupled.jl
index ad2b4a81e..147cfab89 100644
--- a/src/reconstruction/CoreModelCoupled.jl
+++ b/src/reconstruction/CoreModelCoupled.jl
@@ -1,13 +1,5 @@
 """
-    add_reactions(
-        m::CoreModelCoupled,
-        s::V1,
-        b::V2,
-        c::AbstractFloat,
-        xl::AbstractFloat,
-        xu::AbstractFloat;
-        check_consistency = false,
-    ) where {V1<:VecType,V2<:VecType}
+$(TYPEDSIGNATURES)
 
 Add reaction(s) to a `CoreModelCoupled` model `m`.
 
@@ -31,17 +23,7 @@ function add_reactions(
 end
 
 """
-    add_reactions(
-        m::CoreModelCoupled,
-        s::V1,
-        b::V2,
-        c::AbstractFloat,
-        xl::AbstractFloat,
-        xu::AbstractFloat,
-        rxn::String,
-        mets::K;
-        check_consistency = false,
-    ) where {V1<:VecType,V2<:VecType,K<:StringVecType}
+$(TYPEDSIGNATURES)
 
 """
 function add_reactions(
@@ -75,16 +57,7 @@ function add_reactions(
 end
 
 """
-    add_reactions(
-        m::CoreModelCoupled,
-        Sp::M,
-        b::V,
-        c::V,
-        xl::V,
-        xu::V;
-        check_consistency = false,
-    ) where {M<:MatType,V<:VecType}
-
+$(TYPEDSIGNATURES)
 """
 function add_reactions(
     m::CoreModelCoupled,
@@ -105,10 +78,9 @@ function add_reactions(
 end
 
 """
-    add_reactions(m1::CoreModelCoupled, m2::CoreModel; check_consistency = false)
+$(TYPEDSIGNATURES)
 
 Add all reactions from `m2` to `m1`.
-
 """
 function add_reactions(m1::CoreModelCoupled, m2::CoreModel; check_consistency = false)
     new_lm = add_reactions(m1.lm, m2, check_consistency = check_consistency)
@@ -121,18 +93,7 @@ function add_reactions(m1::CoreModelCoupled, m2::CoreModel; check_consistency =
 end
 
 """
-    add_reactions(
-        m::CoreModelCoupled,
-        Sp::M,
-        b::V,
-        c::V,
-        xl::V,
-        xu::V,
-        rxns::K,
-        mets::K;
-        check_consistency = false,
-    ) where {M<:MatType,V<:VecType,K<:StringVecType}
-
+$(TYPEDSIGNATURES)
 """
 function add_reactions(
     m::CoreModelCoupled,
@@ -165,7 +126,7 @@ function add_reactions(
 end
 
 """
-    add_coupling_constraints(m::CoreCoupling, args...)
+$(TYPEDSIGNATURES)
 
 Add constraints of the following form to CoreCoupling and return the modified
 model.
@@ -179,7 +140,7 @@ function add_coupling_constraints(m::CoreCoupling, args...)
 end
 
 """
-    add_coupling_constraints(m::CoreModel, args...)
+$(TYPEDSIGNATURES)
 
 Add coupling constraints to a plain [`CoreModel`](@ref) (returns a
 [`CoreModelCoupled`](@ref)).
@@ -187,12 +148,7 @@ Add coupling constraints to a plain [`CoreModel`](@ref) (returns a
 add_coupling_constraints(m::CoreModel, args...) = CoreModelCoupled(m, args...)
 
 """
-    add_coupling_constraints!(
-        m::CoreCoupling,
-        c::VecType,
-        cl::AbstractFloat,
-        cu::AbstractFloat,
-    )
+$(TYPEDSIGNATURES)
 
 Overload for adding a single coupling constraint.
 """
@@ -206,12 +162,7 @@ function add_coupling_constraints!(
 end
 
 """
-    add_coupling_constraints!(
-        m::CoreCoupling,
-        C::MatType,
-        cl::V,
-        cu::V,
-    ) where {V<:VecType}
+$(TYPEDSIGNATURES)
 
 In-place add a single coupling constraint in form
 ```
@@ -237,7 +188,7 @@ function add_coupling_constraints!(
 end
 
 """
-    remove_coupling_constraints(m::CoreCoupling, args...)
+$(TYPEDSIGNATURES)
 
 Remove coupling constraints from the linear model, and return the modified
 model. Arguments are the same as for in-place version
@@ -250,7 +201,7 @@ function remove_coupling_constraints(m::CoreCoupling, args...)
 end
 
 """
-    remove_coupling_constraints!(m::CoreCoupling, constraint::Int)
+$(TYPEDSIGNATURES)
 
 Removes a single coupling constraints from a [`CoreCoupling`](@ref) in-place.
 """
@@ -259,7 +210,7 @@ remove_coupling_constraints!(m::CoreCoupling, constraint::Int) =
 
 
 """
-    remove_coupling_constraints!(m::CoreCoupling, constraints::Vector{Int})
+$(TYPEDSIGNATURES)
 
 Removes a set of coupling constraints from a [`CoreCoupling`](@ref)
 in-place.
@@ -273,12 +224,7 @@ function remove_coupling_constraints!(m::CoreCoupling, constraints::Vector{Int})
 end
 
 """
-    change_coupling_bounds!(
-        model::CoreCoupling,
-        constraints::Vector{Int};
-        cl::V = Float64[],
-        cu::V = Float64[],
-    ) where {V<:VecType}
+$(TYPEDSIGNATURES)
 
 Change the lower and/or upper bounds (`cl` and `cu`) for the given list of
 coupling constraints.
@@ -425,11 +371,7 @@ end
 end
 
 """
-    change_objective!(
-        model::CoreCoupling,
-        args...;
-        kwargs...,
-    )
+$(TYPEDSIGNATURES)
 
 Forwards arguments to [`change_objective!`](@ref) of the internal model.
 """
diff --git a/src/reconstruction/Reaction.jl b/src/reconstruction/Reaction.jl
index 11976d25a..3fe897e1b 100644
--- a/src/reconstruction/Reaction.jl
+++ b/src/reconstruction/Reaction.jl
@@ -1,6 +1,10 @@
-
 """
+$(TYPEDEF)
+
 A small helper type for constructing reactions inline
+
+# Fields
+$(TYPEDFIELDS)
 """
 struct _Stoichiometry
     s::Dict{String,Float64}
@@ -14,10 +18,11 @@ Base.convert(::Type{_Stoichiometry}, m::Metabolite) = _Stoichiometry(Dict(m.id =
 Base.:*(a::Real, m::Metabolite) = _Stoichiometry(Dict(m.id => a))
 
 """
-    metabolite1 + metabolite2
+$(TYPEDSIGNATURES)
 
-Add 2 groups of [`Metabolite`](@ref)s together to form reactions inline. Use
-with `+`, `*`, [`→`](@ref) and similar operators.
+Shorthand for `metabolite1 + metabolite2`. Add 2 groups of [`Metabolite`](@ref)s
+together to form reactions inline. Use with `+`, `*`, [`→`](@ref) and similar
+operators.
 """
 function Base.:+(a::_Stoichiometrizable, b::_Stoichiometrizable)
     ad = convert(_Stoichiometry, a).s
@@ -30,6 +35,9 @@ function Base.:+(a::_Stoichiometrizable, b::_Stoichiometrizable)
     )
 end
 
+"""
+$(TYPEDSIGNATURES)
+"""
 function _make_reaction_dict(r, p)
     rd = convert(_Stoichiometry, r).s
     pd = convert(_Stoichiometry, p).s
@@ -39,26 +47,28 @@ function _make_reaction_dict(r, p)
 end
 
 """
-    substrates → products
+$(TYPEDSIGNATURES)
 
-Make a forward-only [`Reaction`](@ref) from `substrates` and `products`.
+Shorthand for `substrates → products`. Make a forward-only [`Reaction`](@ref)
+from `substrates` and `products`.
 """
 →(substrates::Maybe{_Stoichiometrizable}, products::Maybe{_Stoichiometrizable}) =
     Reaction("", _make_reaction_dict(substrates, products), :forward)
 
 """
-    substrates ← products
+$(TYPEDSIGNATURES)
 
-Make a reverse-only [`Reaction`](@ref) from `substrates` and `products`.
+Shorthand for `substrates ← products`. Make a reverse-only [`Reaction`](@ref)
+from `substrates` and `products`.
 """
 ←(substrates::Maybe{_Stoichiometrizable}, products::Maybe{_Stoichiometrizable}) =
     Reaction("", _make_reaction_dict(substrates, products), :reverse)
 
 """
-    substrates ↔ products
+$(TYPEDSIGNATURES)
 
-Make a bidirectional (reversible) [`Reaction`](@ref) from `substrates` and
-`products`.
+Shorthand for `substrates ↔ products`. Make a bidirectional (reversible)
+[`Reaction`](@ref) from `substrates` and `products`.
 """
 ↔(substrates::Maybe{_Stoichiometrizable}, products::Maybe{_Stoichiometrizable}) =
     Reaction("", _make_reaction_dict(substrates, products), :bidirectional)
diff --git a/src/reconstruction/SerializedModel.jl b/src/reconstruction/SerializedModel.jl
index c7564c43f..8f9d66f2c 100644
--- a/src/reconstruction/SerializedModel.jl
+++ b/src/reconstruction/SerializedModel.jl
@@ -10,7 +10,7 @@
 @_serialized_change_unwrap remove_reactions
 
 """
-    unwrap_serialized(model::Serialized)
+$(TYPEDSIGNATURES)
 
 Returns the model stored in the serialized structure.
 """
diff --git a/src/reconstruction/StandardModel.jl b/src/reconstruction/StandardModel.jl
index c3f6c792f..89c77f8eb 100644
--- a/src/reconstruction/StandardModel.jl
+++ b/src/reconstruction/StandardModel.jl
@@ -1,5 +1,5 @@
 """
-    add_reactions!(model::StandardModel, rxns::Vector{Reaction})
+$(TYPEDSIGNATURES)
 
 Add `rxns` to `model` based on reaction `id`.
 """
@@ -11,14 +11,14 @@ function add_reactions!(model::StandardModel, rxns::Vector{Reaction})
 end
 
 """
-    add_reaction!(model::StandardModel, rxn::Reaction)
+$(TYPEDSIGNATURES)
 
 Add `rxn` to `model` based on reaction `id`.
 """
 add_reaction!(model::StandardModel, rxn::Reaction) = add_reactions!(model, [rxn])
 
 """
-    add_metabolites!(model::StandardModel, mets::Vector{Metabolite})
+$(TYPEDSIGNATURES)
 
 Add `mets` to `model` based on metabolite `id`.
 """
@@ -30,14 +30,14 @@ function add_metabolites!(model::StandardModel, mets::Vector{Metabolite})
 end
 
 """
-    add_metabolite!(model::StandardModel, met::Metabolite)
+$(TYPEDSIGNATURES)
 
 Add `met` to `model` based on metabolite `id`.
 """
 add_metabolite!(model::StandardModel, met::Metabolite) = add_metabolites!(model, [met])
 
 """
-    add_genes!(model::StandardModel, genes::Vector{Gene})
+$(TYPEDSIGNATURES)
 
 Add `genes` to `model` based on gene `id`.
 """
@@ -49,14 +49,14 @@ function add_genes!(model::StandardModel, genes::Vector{Gene})
 end
 
 """
-    add_gene!(model::StandardModel, genes::Gene)
+$(TYPEDSIGNATURES)
 
 Add `gene` to `model` based on gene `id`.
 """
 add_gene!(model::StandardModel, gene::Gene) = add_genes!(model, [gene])
 
 """
-    @add_reactions!(model::Symbol, ex::Expr)
+$(TYPEDSIGNATURES)
 
 Shortcut to add multiple reactions and their lower and upper bounds
 
@@ -110,11 +110,7 @@ macro add_reactions!(model::Symbol, ex::Expr)
 end
 
 """
-    remove_genes!(
-        model::StandardModel,
-        ids::Vector{String};
-        knockout_reactions::Bool = false,
-    )
+$(TYPEDSIGNATURES)
 
 Remove all genes with `ids` from `model`. If `knockout_reactions` is true, then also
 constrain reactions that require the genes to function to carry zero flux.
@@ -144,11 +140,7 @@ function remove_genes!(
 end
 
 """
-    remove_gene!(
-        model::StandardModel,
-        id::Vector{String};
-        knockout_reactions::Bool = false,
-    )
+$(TYPEDSIGNATURES)
 
 Remove gene with `id` from `model`. If `knockout_reactions` is true, then also
 constrain reactions that require the genes to function to carry zero flux.
@@ -244,11 +236,7 @@ end
 end
 
 """
-    change_objective!(
-        model::StandardModel,
-        rxn_ids::Vector{String};
-        weights = ones(length(rxn_ids)),
-    )
+$(TYPEDSIGNATURES)
 
 Change the objective for `model` to reaction(s) with `rxn_ids`, optionally specifying their `weights`. By default,
 assume equal weights. If no objective exists in model, sets objective.
diff --git a/src/reconstruction/community.jl b/src/reconstruction/community.jl
index 3271f0eba..a2ca155a5 100644
--- a/src/reconstruction/community.jl
+++ b/src/reconstruction/community.jl
@@ -1,9 +1,5 @@
 """
-    add_community_objective!(
-        community::CoreModel,
-        objective_mets_weights::Dict{String, Float64};
-        objective_id = "community_biomass",
-    )
+$(TYPEDSIGNATURES)
 
 Add an objective column to the `community` model with optional id `objective_id`. Supply a
 dictionary mapping the string names of the objective metabolites to their weights in
@@ -41,11 +37,7 @@ function add_community_objective!(
 end
 
 """
-    add_community_objective!(
-        community::StandardModel,
-        objective_mets_weights::Dict{String, Float64};
-        objective_id = "community_biomass"
-    )
+$(TYPEDSIGNATURES)
 
 Variant of [`add_community_objective!`] that takes a `StandardModel` community model as input.
 """
@@ -69,11 +61,7 @@ function add_community_objective!(
 end
 
 """
-    update_community_objective!(
-        community::CoreModel,
-        objective_id::String,
-        objective_mets_weights::Dict{String, Float64}
-    )
+$(TYPEDSIGNATURES)
 
 Update the weights for the objective column with id `objective_id` in `community` using
 `objective_mets_weights`, which maps metabolite ids to weights. The current weights are
@@ -110,11 +98,7 @@ function update_community_objective!(
 end
 
 """
-    update_community_objective!(
-        community::StandardModel,
-        objective_id::String,
-        objective_mets_weights::Dict{String, Float64}
-    )
+$(TYPEDSIGNATURES)
 
 Variant of [`update_community_objective!`] that takes a `StandardModel` community model as input.
 """
@@ -130,13 +114,7 @@ function update_community_objective!(
 end
 
 """
-    join_with_exchanges(
-        ::Type{CoreModel},
-        models::Vector{M},
-        exchange_rxn_mets::Dict{String,String};
-        biomass_ids = String[],
-        model_names = String[],
-    ) where {M<:MetabolicModel}
+$(TYPEDSIGNATURES)
 
 Return a `CoreModel` representing the community model of `models` joined through their
 exchange reactions and metabolites in the dictionary `exchange_rxn_mets`, which maps
@@ -324,13 +302,7 @@ function join_with_exchanges(
 end
 
 """
-    join_with_exchanges(
-        ::Type{StandardModel},
-        models::Vector{M},
-        exchange_rxn_mets::Dict{String,String};
-        biomass_ids = [],
-        model_names = [],
-    )::StandardModel where {M<:MetabolicModel}
+$(TYPEDSIGNATURES)
 
 A variant of [`join_with_exchanges`](@ref) that returns a `StandardModel`.
 """
@@ -385,13 +357,7 @@ function join_with_exchanges(
 end
 
 """
-    add_model_with_exchanges(
-        community::CoreModel,
-        model::MetabolicModel,
-        exchange_rxn_mets::Dict{String,String};
-        model_name = "unknown_species",
-        biomass_id = nothing,
-    )
+$(TYPEDSIGNATURES)
 
 Add `model` to `community`, which is a pre-existing community model with exchange reactions
 and metabolites in the dictionary `exchange_rxn_mets`. The `model_name` is appended to each
@@ -501,13 +467,7 @@ function add_model_with_exchanges(
 end
 
 """
-    add_model_with_exchanges!(
-        community::StandardModel,
-        model::MetabolicModel,
-        exchange_rxn_mets::Dict{String,String};
-        model_name = "unknown_species",
-        biomass_id = nothing,
-    )
+$(TYPEDSIGNATURES)
 
 The `StandardModel` variant of [`add_model_with_exchanges`](@ref), but is in-place.
 """
@@ -558,13 +518,7 @@ function add_model_with_exchanges!(
 end
 
 """
-    add_model_with_exchanges(
-        community::StandardModel,
-        model::MetabolicModel,
-        exchange_rxn_mets::Dict{String,String};
-        model_name = "unknown_species",
-        biomass_id = nothing,
-    )
+$(TYPEDSIGNATURES)
 
 The `StandardModel` variant of [`add_model_with_exchanges`](@ref). Makes a deepcopy of
 `community` and calls the inplace variant of this function on that copy.
diff --git a/src/reconstruction/enzymes.jl b/src/reconstruction/enzymes.jl
index 09dac3dc6..6f22cf800 100644
--- a/src/reconstruction/enzymes.jl
+++ b/src/reconstruction/enzymes.jl
@@ -1,5 +1,5 @@
 """
-    with_smoment(; kwargs...)
+$(TYPEDSIGNATURES)
 
 Specifies a model variant which adds extra semantics of the sMOMENT algorithm,
 giving a [`SMomentModel`](@ref). The arguments are forwarded to
@@ -8,7 +8,7 @@ giving a [`SMomentModel`](@ref). The arguments are forwarded to
 with_smoment(; kwargs...) = model -> make_smoment_model(model; kwargs...)
 
 """
-    with_gecko(; kwargs...)
+$(TYPEDSIGNATURES)
 
 Specifies a model variant which adds extra semantics of the Gecko algorithm,
 giving a [`GeckoModel`](@ref). The arguments are forwarded to
diff --git a/src/reconstruction/gapfill_minimum_reactions.jl b/src/reconstruction/gapfill_minimum_reactions.jl
index b3383f1f0..e3828e8fb 100644
--- a/src/reconstruction/gapfill_minimum_reactions.jl
+++ b/src/reconstruction/gapfill_minimum_reactions.jl
@@ -1,13 +1,5 @@
 """
-    function gapfill_minimum_reactions(
-        model::MetabolicModel,
-        universal_reactions::Vector{Reaction},
-        optimizer;
-        objective_bounds = (_constants.tolerance, _constants.default_reaction_bound),
-        maximum_new_reactions = 5,
-        weights = fill(1.0, length(universal_reactions)),
-        modifications = [],
-    )
+$(TYPEDSIGNATURES)
 
 Find a minimal set of reactions from `universal_reactions` that should be added
 to `model` so that the model has a feasible solution with bounds on its
@@ -27,6 +19,28 @@ information in Julia datatypes.
 To reduce the uncertainty in the MILP solver (and likely reduce the
 complexity), you may put a limit on the size of the added reaction set in
 `maximum_new_reactions`.
+
+# Common pitfalls
+
+If [`gapfill_minimum_reactions`](@ref) is supposed to generate any reasonable
+output, the input model *MUST NOT* be feasible, otherwise there is "no work to
+do" and no reactions are added. Notably, an inactive model (the flux is zero)
+is considered to be feasible. If this is the case, [`gapfilled_rids`](@ref)
+will return an empty vector (as opposed to `nothing`).
+
+To prevent this, you may need to modify the model to disallow the trivial
+solutions (for example by putting a lower bound on reactions that you expect to
+be working in the solved model, in a similar manner like how the ATP
+maintenance reaction is bounded in E. Coli "core" model). The
+`objective_bounds` parameter makes this easier by directly placing a bound on
+the objective value of the model, which typically forces the model to be
+active.
+
+The `maximum_new_reactions` parameter may have critical impact on performance
+in some solvers, because (in a general worst case) there is
+`2^maximum_new_reactions` model variants to be examined. Putting a hard limit
+on the reaction count may serve as a heuristic that helps the solver not to
+waste too much time solving impractically complex subproblems.
 """
 function gapfill_minimum_reactions(
     model::MetabolicModel,
@@ -104,13 +118,18 @@ function gapfill_minimum_reactions(
 end
 
 """
-    gapfilled_mask(opt_model::BitVector)
+$(TYPEDSIGNATURES)
 
 Get a `BitVector` of added reactions from the model solved by
 [`gapfill_minimum_reactions`](@ref). The bit indexes correspond to the indexes
 of `universal_reactions` given to the gapfilling function. In case the model is
 not solved, this returns `nothing`.
 
+If this function returns a zero vector (instead of `nothing`), it is very
+likely that the original model was already feasible and you may need to
+constraint it more. Refer to "pitfalls" section in the documentation of
+[`gapfill_minimum_reactions`](@ref) for more details.
+
 # Example
 
     gapfill_minimum_reactions(myModel, myReactions, Tulip.Optimizer) |> gapfilled_mask
@@ -119,11 +138,16 @@ gapfilled_mask(opt_model)::BitVector =
     is_solved(opt_model) ? value.(opt_model[:y]) .> 0 : nothing
 
 """
-    gapfilled_rids(opt_model, universal_reactions::Vector{Reaction})::Vector{String}
+$(TYPEDSIGNATURES)
 
 Utility to extract a short vector of IDs of the reactions added by the
 gapfilling algorithm. Use with `opt_model` returned from
 [`gapfill_minimum_reactions`](@ref).
+
+If this function returns an empty vector (instead of `nothing`), it is very
+likely that the original model was already feasible and you may need to
+constraint it more. Refer to "pitfalls" section in the documentation of
+[`gapfill_minimum_reactions`](@ref) for more details.
 """
 gapfilled_rids(opt_model, universal_reactions::Vector{Reaction}) =
     let v = gapfilled_mask(opt_model)
@@ -131,7 +155,7 @@ gapfilled_rids(opt_model, universal_reactions::Vector{Reaction}) =
     end
 
 """
-    gapfilled_rids(universal_reactions::Vector{Reaction})
+$(TYPEDSIGNATURES)
 
 Overload of [`gapfilled_rids`](@ref) that can be piped easily.
 
@@ -143,10 +167,7 @@ gapfilled_rids(universal_reactions::Vector{Reaction}) =
     opt_model -> gapfilled_rids(opt_model, universal_reactions)
 
 """
-    _universal_stoichiometry(
-        universal_reactions::Vector{Reaction},
-        mids,
-    )
+$(TYPEDSIGNATURES)
 
 A helper function that constructs the stoichiometric matrix of a set of
 `universal_reactions`. The order of the metabolites is determined with
diff --git a/src/reconstruction/modifications/generic.jl b/src/reconstruction/modifications/generic.jl
index accac4048..383366500 100644
--- a/src/reconstruction/modifications/generic.jl
+++ b/src/reconstruction/modifications/generic.jl
@@ -1,5 +1,5 @@
 """
-    with_changed_bound(args...; kwargs...)
+$(TYPEDSIGNATURES)
 
 Specifies a model variant that has a new bound set. Forwards arguments to
 [`change_bound`](@ref). Intended for usage with [`screen`](@ref).
@@ -7,7 +7,7 @@ Specifies a model variant that has a new bound set. Forwards arguments to
 with_changed_bound(args...; kwargs...) = m -> change_bound(m, args...; kwargs...)
 
 """
-    with_changed_bounds(args...; kwargs...)
+$(TYPEDSIGNATURES)
 
 Specifies a model variant that has new bounds set. Forwards arguments to
 [`change_bounds`](@ref). Intended for usage with [`screen`](@ref).
@@ -15,7 +15,7 @@ Specifies a model variant that has new bounds set. Forwards arguments to
 with_changed_bounds(args...; kwargs...) = m -> change_bounds(m, args...; kwargs...)
 
 """
-    with_removed_metabolite(args...; kwargs...)
+$(TYPEDSIGNATURES)
 
 Specifies a model variant without a certain metabolite. Forwards arguments to
 [`remove_metabolite`](@ref). Intended to be used with [`screen`](@ref).
@@ -23,7 +23,7 @@ Specifies a model variant without a certain metabolite. Forwards arguments to
 with_removed_metabolite(args...; kwargs...) = m -> remove_metabolite(m, args...; kwargs...)
 
 """
-    with_removed_metabolites(args...; kwargs...)
+$(TYPEDSIGNATURES)
 
 Plural version of [`with_removed_metabolite`](@ref), calls
 [`remove_metabolites`](@ref) internally.
@@ -32,7 +32,7 @@ with_removed_metabolites(args...; kwargs...) =
     m -> remove_metabolites(m, args...; kwargs...)
 
 """
-    with_added_reactions(args...; kwargs...)
+$(TYPEDSIGNATURES)
 
 Specifies a model variant with reactions added. Forwards the arguments to
 [`add_reactions`](@ref). Intended to be used with [`screen`](@ref).
@@ -40,7 +40,7 @@ Specifies a model variant with reactions added. Forwards the arguments to
 with_added_reactions(args...; kwargs...) = m -> add_reactions(m, args...; kwargs...)
 
 """
-    with_removed_reaction(args...; kwargs...)
+$(TYPEDSIGNATURES)
 
 Specifies a model variant without a certain reaction. Forwards arguments to
 [`remove_reaction`](@ref). Intended to be used with [`screen`](@ref).
@@ -48,7 +48,7 @@ Specifies a model variant without a certain reaction. Forwards arguments to
 with_removed_reaction(args...; kwargs...) = m -> remove_reaction(m, args...; kwargs...)
 
 """
-    with_removed_reactions(args...; kwargs...)
+$(TYPEDSIGNATURES)
 
 Plural version of [`with_removed_reaction`](@ref), calls
 [`remove_reactions`](@ref) internally.
diff --git a/test/analysis/minimize_metabolic_adjustment.jl b/test/analysis/minimize_metabolic_adjustment.jl
index 2759d1cfc..dbb115992 100644
--- a/test/analysis/minimize_metabolic_adjustment.jl
+++ b/test/analysis/minimize_metabolic_adjustment.jl
@@ -6,8 +6,8 @@
     moma = minimize_metabolic_adjustment_analysis_dict(
         model,
         sol,
-        OSQP.Optimizer;
-        modifications = [silence, change_optimizer_attribute("polish", true)],
+        Clarabel.Optimizer;
+        modifications = [silence],
     )
 
     @test isapprox(moma["biomass1"], 0.07692307692307691, atol = QP_TEST_TOLERANCE)
diff --git a/test/analysis/parsimonious_flux_balance_analysis.jl b/test/analysis/parsimonious_flux_balance_analysis.jl
index 341eb6c43..a0775e51f 100644
--- a/test/analysis/parsimonious_flux_balance_analysis.jl
+++ b/test/analysis/parsimonious_flux_balance_analysis.jl
@@ -8,11 +8,7 @@
             change_constraint("EX_m1(e)", lb = -10.0),
             change_optimizer_attribute("IPM_IterationsLimit", 500),
         ],
-        qp_modifications = [
-            change_optimizer(OSQP.Optimizer),
-            change_optimizer_attribute("polish", true),
-            silence,
-        ],
+        qp_modifications = [change_optimizer(Clarabel.Optimizer), silence],
     )
 
     # The used optimizer doesn't really converge to the same answer everytime
diff --git a/test/base/utils/looks_like.jl b/test/base/utils/looks_like.jl
index c09966d33..6b88dccb5 100644
--- a/test/base/utils/looks_like.jl
+++ b/test/base/utils/looks_like.jl
@@ -76,3 +76,11 @@ end
     @test length(filter(looks_like_biomass_reaction, reactions(model))) == 1
     @test length(filter(looks_like_extracellular_metabolite, metabolites(model))) == 20
 end
+
+@testset "Ontology usage in is_xxx_reaction" begin
+    model = load_model(StandardModel, model_paths["e_coli_core.json"])
+
+    # macro generated, so only test positive and negative case
+    @test !is_biomass_reaction(model, "PFL")
+    @test is_biomass_reaction(model, "BIOMASS_Ecoli_core_w_GAM")
+end
diff --git a/test/runtests.jl b/test/runtests.jl
index 847c7f35e..e19df54f1 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -8,7 +8,7 @@ using JuMP
 using LinearAlgebra
 using MAT
 using OrderedCollections
-using OSQP
+using Clarabel
 using SHA
 using SparseArrays
 using Statistics
@@ -18,7 +18,7 @@ using GLPK # for MILPs
 # tolerance for comparing analysis results (should be a bit bigger than the
 # error tolerance in computations)
 TEST_TOLERANCE = 10 * COBREXA._constants.tolerance
-QP_TEST_TOLERANCE = 1e-2 # for OSQP
+QP_TEST_TOLERANCE = 1e-2 # for Clarabel
 
 print_timing(fn, t) = @info "$(fn) done in $(round(t; digits = 2))s"