jane* docs
Start hereWorkflows

Optimize a coding sequence for expression in another host

The route: measure what the CDS looks like to the host you are moving it to → decide what must not change → optimize → measure the same things again → check what you gave up.

Every number below came from running the engines on the ADH1 CDS of data/samples/Genbank/SC_ADH1.gb — the real Saccharomyces cerevisiae alcohol dehydrogenase I gene, CDS at 751..1797, 1,047 bp, 349 codons — which the Codon usage starter opens.


Step 1 — Measure the sequence against the host you are leaving, and the one you are going to

Tool: Codon Usage Analysis. Where: Tools → Analyze.

Select the CDS and run it twice, once per organism. The same 1,047 bp gives two very different readings:

S. cerevisiae (its own host)E. coli
GC0.4840.484
Codons349349
Rare codons0 (0.0%)12 (3.4%)
Which ones—AGA ×8, CTA ×3, CCC ×1
CAI0.812not available
Start codon usage1—
N-end-rule half-life> 30 h—

That is the whole argument for codon optimization in one table. A gene perfectly adapted to its own host has a dozen codons that E. coli barely uses, eight of them the same one — AGA, an arginine codon whose tRNA is scarce in E. coli and a classic cause of stalled translation and truncation products.

The empty CAI cell is not a bug and is not filled in. The bundled pydna reference tables carry relative-adaptiveness weights for S. cerevisiae only, so a CAI for E. coli would have to be invented. The panel reports null and says why. The rare-codon scan and GC work for both organisms because those tables do exist for both.

Check: the region is a real, in-frame CDS. A codon-level analysis of a length that is not a multiple of three is refused up front, with the length in the message, rather than silently analysing a frameshift.


Step 2 — Choose the host table, and know where it came from

Tool: Codon Tables.

Three reference tables ship for instant use — E. coli K-12, H. sapiens, S. cerevisiae — Kazusa-derived relative synonymous frequencies.

They are the offline fallback, not the primary path. The intended route computes a table live from the record's own organism → its current NCBI reference assembly → real CDS. That matters because a bundled table is a cache that ages, and because three hosts is not a list of the organisms anyone might express in. The bundled set exists because Kazusa cannot be called directly from a browser, so a no-network default is needed; it is explicitly not meant to grow into an organism database. Add an organism by loading its sequences, not by picking from a menu of three.

You can also build a table from sequences you already have, which is what you want when your expression host is a strain rather than a species.


Step 3 — Say what must not change

Tool: Codon Optimization. Where: Tools → Design.

This is the step people skip, and it is the one that makes the difference between a sequence you can order and a sequence you have to debug. The optimizer separates constraints (hard — the solve fails rather than violating one) from objectives (soft — maximised, weighted, traded off).

For a CDS you are moving to E. coli, a minimum set:

SpecRoleWhy
EnforceTranslationconstraintThe protein must not change. Non-negotiable
AvoidPattern — BsmBIconstraintYou are going to Golden-Gate this. A new CGTCTC anywhere is a silent disaster
EnforceGCContent 0.35–0.65 in a 50 bp windowconstraintLocal GC, not just global — a synthesis house will refuse a GC-poor or GC-rich window even when the whole gene averages fine
CodonOptimize, method use_best_codonobjectiveThe actual optimization

There are far more available — avoid hairpins, avoid rare codons below a frequency, uniquify all k-mers, enforce a melting temperature, enforce terminal GC, pin a subsequence, budget the number of edits, enforce a minimum number of changes, restrict a region to a set of choices. Each is a real spec of the ported solver, with its own location and priority, and each appears in the before/after evaluation whether it passed or not.

Three codon-optimization methods, not one, and they answer different questions: use_best_codon takes the host's most frequent codon for each residue; the frequency-matching method reproduces the host's distribution rather than its maximum; and harmonize_rca mimics the codon rank the gene had in its original host — which needs the original host's table too, and is the one to reach for when you care about co-translational folding rather than raw yield.


Step 4 — Run it, and read both sides of the ledger

With exactly the four specs above, targeting E. coli K-12, seed 1:

success                 true
length                  1047 bp
edits                   290 bases changed  (27.7%)  in 226 runs
objective score         −177.3  →  −25.3

constraints after:
  PASS  EnforceTranslation[0-1047]                            Enforced by nucleotides restrictions
  PASS  AvoidPattern[0-1047](BsmBI CGTCTC)                    Pattern not found
  PASS  EnforceGCContent[0-1047](0.35–0.65, window 50)        Passed

And the same measurements from step 1, re-run on the output:

BeforeAfter
Rare codons for E. coli12 (AGA×8, CTA×3, CCC×1)0
GC0.4840.546
CAI for S. cerevisiae0.8120.588
Protein (349 aa)MSIPETQKGVIFYESHGKLE…identical

Read the last two rows together. The sequence got substantially better for E. coli and substantially worse for yeast — CAI 0.812 → 0.588 — which is exactly right and exactly what you are choosing to do. An optimized gene is optimized for a host; there is no such thing as an optimized sequence.

And the protein is byte-for-byte identical, which is the EnforceTranslation constraint doing its job rather than a hope.

Check: success: true and every constraint reading PASS. A failed solve names the unsatisfiable constraint instead of returning a best-effort sequence — which is the right behaviour, because a sequence that quietly violates your BsmBI constraint is worse than no sequence.

Note what Enforced by nucleotides restrictions means on the first line, because it is the one row here that is not a measurement. EnforceTranslation is satisfied by construction: the solver only ever considered codons encoding the same residue, so the constraint cannot fail and was never tested against your starting sequence. In the panel that row reads guaranteed, not measured for exactly this reason. It is a true statement about the output and says nothing about the input — the edit count is what tells you how much changed.


Step 5 — Look at what actually changed, and get alternatives

The result carries the edits as runs of changed bases, so the diff draws on the map rather than being a wall of two sequences. 226 runs across 1,047 bp is roughly one change every 4–5 bases, which is what a synonymous recode looks like.

If you want more than one candidate, there is a knob for it, and it is honest about why. The solver enumerates exhaustively whenever a localized sub-problem is smaller than the randomization threshold (default 10,000) — and at that default essentially every sub-problem is, so the solve is deterministic. Running it five times would give you the same sequence five times. Lowering the threshold forces the genuinely stochastic search paths, which return different sequences satisfying the same constraints. The panel reports how much the candidates actually differed rather than assuming they did.

One caveat worth stating: the seed makes runs reproducible within this engine. It does not reproduce DnaChisel's numpy stream, so a seeded run here will not match a seeded Python run.


Step 6 — Check it before you order it

Round-trip the optimized sequence through the tools you would have used anyway:

  • Codon Usage Analysis against the target host — rare codons should be gone, and you should look at whether you also removed the ones near the 5′ end that slow initiation on purpose.
  • Restriction Digest — confirm the sites you need are still there and the ones you forbade are not. AvoidPattern covers what you named; it cannot know about the enzyme you did not mention.
  • Properties and Protein Analysis — the translation should be unchanged, and it is worth proving rather than trusting.
  • Back-translate if you are starting from protein rather than DNA; it reads the same host tables from the same place, so it cannot disagree with the optimizer.

What you did, and where each piece lives

StepToolHand-off
1Codon Usage AnalysisRare-codon census + CAI, per organism
2Codon TablesThe host table, bundled or live or built
3–4Codon OptimizationAn optimized CDS with every constraint evaluated
5Same panelThe edit runs, drawn on the map; alternative candidates
6Digest · Properties · Protein AnalysisConfirmation before you spend money

If it does not work

SymptomWhere to look
"not a multiple of 3"Your region is not an in-frame CDS. The message gives the length it saw
The solve failsA constraint is unsatisfiable. It is named — usually GC bounds too tight for the window size, or a forbidden pattern the protein forces
CAI is unavailableOnly S. cerevisiae has CAI weights in the bundled pydna tables. The rare-codon scan still works
Five runs give five identical sequencesExpected. Lower the randomization threshold to force the stochastic paths
A seeded run does not match your Python runIt will not. The seed is reproducible within this engine only
The optimized gene expresses worseCheck whether you flattened the 5′ end. Maximum-codon optimization is not always the right objective — harmonize_rca exists for exactly this

The tools, in depth: Codon Optimization · Codon Usage Analysis · Codon Tables · Back-translate

What follows: RBS & Operon Design for the expression context around the CDS, then Golden Gate or clone-and-verify to build it.

The canvas this draws on: the sequence map.

On this page