Matlab Modernize Code
MathWorksOfficialCreated by MathWorks
- Skill
- Tool integration
- Software development
- BSD-2-Clause
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a systematic approach to modernizing MATLAB codebases. It acts as a resolver for issues identified by the check_matlab_code MCP tool, which flags functions that are removed, discouraged, or slated for future removal. By replacing outdated APIs and anti-patterns with current, performant, and secure alternatives, this skill helps maintain code longevity and compatibility with modern MATLAB releases.
What it does
- Identifies and replaces removed or discouraged MATLAB functions.
- Refactors anti-patterns like eval, clear all, and str2num.
- Provides migration paths for file I/O, UI components, and signal processing.
- Integrates with static analysis results from check_matlab_code.
- Offers domain-specific guidance for deep learning, audio/video, and communications.
- Promotes modern design patterns like table-based workflows and arguments blocks.
When to use it
- When check_matlab_code reports lifecycle diagnostics (info, warning, or error).
- When migrating legacy scripts to newer MATLAB versions.
- When replacing discouraged patterns like containers.Map or bsxfun.
- When updating code to use modern APIs like readmatrix or datetime.
- When starting new projects to ensure best-practice implementation from the start.
Advantages
- Ensures code compatibility with current and future MATLAB releases.
- Improves code performance and readability by using optimized modern APIs.
- Enhances security by eliminating dangerous patterns like unrestricted eval.
- Reduces technical debt by systematically removing deprecated dependencies.
- Provides clear, tested migration paths for common domain-specific tasks.
Usage examples
- “Modernize this legacy file I/O code: [M, t] = xlsread('data.xlsx');”
- The agent replaces the call with the modern readtable function: T = readtable('data.xlsx');
- “How do I replace this discouraged eval pattern: eval(['A' int2str(n) '=magic(n);']);”
- The agent suggests using a cell array for dynamic indexing: A = cell(10,1); for n = 1:10, A{n} = magic(n); end
- “Update my audio code that uses the removed wavread function.”
- The agent provides the modern replacement using audioread, including handling for sample ranges and native formats.
- “Replace my old BPSK modulator object with the modern function call.”
- The agent replaces the comm.BPSKModulator object instantiation with the direct pskmod(data, 2) function call.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/matlab/matlab-modernize-code/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip matlab-modernize-code.zip -d .agents/skills
Documentation by MathWorks
Code Modernization
Replace removed or discouraged MATLAB functions and anti-patterns with modern equivalents. This skill is the resolver — the MCP tool check_matlab_code (MATLAB's Code Analyzer) is the detector.
Terms used in this skill:
Removed — the function is no longer in MATLAB; calling it errors.
Discouraged — the function still works, but a better API exists. It may or may not be slated for removal in a future release.
When to Use
check_matlab_codereports a lifecycle diagnostic — any of these three wordings, matched case-insensitively (STRMATCH is not recommendedis the same signal as the lowercase form):'X' is not recommended...(severity: info)'X' will be removed in a future release...(severity: warning)'X' has been removed...(severity: error — the code errors today)
Code uses a discouraged pattern Code Analyzer does not flag — e.g.
containers.Map,bsxfun,str2num,eval,clear all— scan the source, not just the diagnostics (see Conventions)User asks to modernize, migrate, or update old MATLAB code
Code uses functions listed in the quick reference table below
After static analysis reveals removed or discouraged API usage
Writing new code in a domain that has known removed or discouraged patterns
When NOT to Use
This skill swaps removed or discouraged APIs for modern equivalents. It does not:
Review code quality broadly
Debug runtime behavior (removed-function errors excepted — those are modernization fixes)
Optimize already-correct code for speed → hand off to
matlab-optimize-performanceWrite or migrate function input/output validation (
argumentsblocks,inputParser/validateattributes→mustBe*validators, repeating args, name-value forwarding) → hand off tomatlab-validate-function-arguments
Quick Reference: Removed, Discouraged & Anti-Patterns
| Topic | Recommendation | Since | Category | Rationale |
|---|---|---|---|---|
csvread / dlmread | readmatrix | R2019a | File I/O | |
csvwrite / dlmwrite | writematrix | R2019a | File I/O | |
xlsread | readtable, readmatrix | R2019a | File I/O | |
xlswrite | writetable, writematrix | R2019a | File I/O | |
datenum / datestr / now | datetime (use string/char to format) | R2022b | Date/Time (Not recommended) | Flagged by Code Analyzer |
eval / evalc / evalin | Dynamic field names s.(name), function handles / feval, cells for sequential names | — | Anti-pattern | Not compiled (slow); can overwrite workspace vars; hard to debug; injection risk |
str2num | str2num(text, Evaluation="restricted") | R2022a | Security | Unrestricted form runs input via eval (injection risk); see references |
uicontrol | uibutton, uidropdown, etc. | R2016a | UI/App | |
guide | appdesigner | R2025a | UI (Removed) | |
strmatch | startsWith, matches | R2019b | Strings | |
clear all | clearvars | — | Workspace | Clears functions from memory; forces recompilation |
datestrnuance:datestr's job was to format a date as text, so its modern replacement isstring(dt)/char(dt)or settingdt.Format— not a baredatetime(...)call. Usedatetimeto replacedatenum/now(the numeric/serial-date path). Neither is removed; both are Code Analyzer "not recommended" as of R2022b.s = datestr(t, 'yyyy-mm-dd HH:MM:SS'); % old s = string(t, 'yyyy-MM-dd HH:mm:ss'); % new — note the specifiers change: % datetime uses MM=month, mm=minute (datestr had mm=month, MM=minute)
Modern Design Patterns
Prefer these in all new code:
Table-Based Workflows
data = readtable('sensors.csv');
data.Timestamp = datetime(data.Timestamp);
data.Status = categorical(data.Status);
recentData = data(data.Timestamp > datetime('today') - days(7), :);
summary = groupsummary(recentData, 'SensorID', 'mean', 'Value');String Arrays (not char arrays)
name = "John"; % not 'John'
names = ["John", "Jane", "Bob"]; % not {'John','Jane','Bob'}
fullName = firstName + " " + lastName; % not [first,' ',last]
idx = contains(names, "Jo"); % not cellfun + strfindArguments Block (not nargin/varargin)
function result = processData(data, options)
arguments
data (:,:) double
options.Method (1,1) string {mustBeMember(options.Method, ["fast","accurate"])} = "fast"
options.Verbose (1,1) logical = false
end
endKey Migrations
File I/O: csvread/xlsread → readmatrix/readtable
% Old → Modern
M = csvread('data.csv'); % M = readmatrix("data.csv");
M = dlmread('data.txt','\t'); % M = readmatrix("data.txt", Delimiter="\t");
[n,t,r] = xlsread('f.xlsx'); % T = readtable("f.xlsx");
csvwrite('out.csv', M); % writematrix(M, "out.csv");
xlswrite('out.xlsx', data); % writetable(T, "out.xlsx");eval → Structured Alternatives
Avoid eval/evalc/evalin: the text isn't compiled (slower), can silently overwrite workspace
variables, and is hard to read/debug. Match the intent to a construct — see
references/core-functions-guidance.md for the full set (sequential names, sprintf filenames,
try/catch).
% Old: eval([varName ' = 42;']); -> dynamic field name
s.(varName) = 42;
% Old: result = eval(['process_' method '(x)']); -> handle dispatch table
handlers.fast = @processFast;
handlers.slow = @processSlow;
result = handlers.(method)(x);
% Old: for n=1:10, eval(['A' int2str(n) '=magic(n);']); end -> index a cell
A = cell(10,1);
for n = 1:10, A{n} = magic(n); endReferences
Load these when working in a specific domain:
| Load when... | Reference |
|---|---|
Core MATLAB functions (file I/O, strings, UI); security anti-patterns (eval/str2num), containers.Map→dictionary, bsxfun→implicit expansion | references/core-functions-guidance.md |
| Deep Learning (trainNetwork→trainnet, LayerGraph/SeriesNetwork/DAGNetwork→dlnetwork, classify, activations) | references/deep-learning-guidance.md |
| Signal processing functions | references/signal-processing-guidance.md |
| Audio/video I/O migration (wavread, aviread) | references/audio-video-guidance.md |
| Optimization toolbox (optimset, optimtool) | references/optimization-guidance.md |
| Control systems plot options | references/control-systems-guidance.md |
| Image processing ROI objects | references/image-processing-guidance.md |
| Statistics/ML (svmtrain, dataset, classregtree) | references/statistics-ml-guidance.md |
| Communications System objects | references/communications-guidance.md |
| Mapping Toolbox (webmap, wmmarker, wmline, geotiffread, mfwdtran, makerefmat) | references/mapping-guidance.md |
Conventions
Always run
check_matlab_codefirst — let static analysis find removed or discouraged usageAfter running it, scan the source for patterns the Code Analyzer may under-flag:
str2num(framed only as a perf tip);containers.Map(see next point).containers.Mapis not flagged bycheck_matlab_code. A plain script using it returns zero diagnostics; acontainers.Mapproperty default surfaces only a handle-default-sharing warning ("all instances share the same object data"). The detect-first workflow will not find this migration — treat anycontainers.Mapusage, and that handle-sharing warning on a property, as acontainers.Map→dictionarytrigger.subplotis not deprecated or discouraged. It is fully supported and is never flagged bycheck_matlab_code.tiledlayout/nexttileis a preferred alternative for new code, not a required migration — do not tell the user to replace workingsubplotcode.Fix removed or discouraged patterns before other code quality issues
When writing new code, use the modern pattern from the start — don't write legacy code and fix it later
A
'X' has been removeddiagnostic (severityerror) means the function errors today — treat it as a break-fix modernization, ahead of "not recommended" (info) and "will be removed" (warning) casesWhen migrating, test the modern replacement against the old behavior to confirm equivalence
Legacy
.mfiles are often Windows-1252 encoded. After an edit, verify non-ASCII characters (°,µ,±) survived — a diff showing an unrelated one-line change on a comment or string is usually a re-encoded byte, not your edit.Consult the domain-specific reference file for detailed migration patterns with code examples
Copyright 2026 The MathWorks, Inc.
Files
- ATTRIBUTION.mdAdded by AutoGineer1.0 KiB
- LICENSELicence file added by AutoGineer1.4 KiB
- SKILL.md9.7 KiB
- manifest.yaml1.1 KiB
- references/audio-video-guidance.md9.4 KiB
- references/communications-guidance.md8.5 KiB
- references/control-systems-guidance.md5.0 KiB
- references/core-functions-guidance.md22.1 KiB
- references/deep-learning-guidance.md3.3 KiB
- references/image-processing-guidance.md6.9 KiB
- references/mapping-guidance.md14.5 KiB
- references/optimization-guidance.md6.0 KiB
- references/signal-processing-guidance.md5.0 KiB
- references/statistics-ml-guidance.md8.0 KiB
references/core-functions-guidance.md
Core Function Modernization
Core MATLAB (base-language) modernizations: File I/O, strings, UI components,
security anti-patterns, key-value containers, and base-language design patterns.
Toolbox-specific migrations live in their own reference files — see the
References table in SKILL.md (deep learning, signal, image, mapping,
statistics, control, optimization, and communications each have a dedicated
file).
Quick Reference: Core Function Mappings
| Topic | Recommendation | Since | Category | Status |
|---|---|---|---|---|
csvread / dlmread | readmatrix | R2019a | File I/O | Not Recommended |
csvwrite / dlmwrite | writematrix | R2019a | File I/O | Not Recommended |
xlsread | readtable / readmatrix / readcell | R2019a | File I/O | Not Recommended |
xlswrite | writetable / writematrix / writecell | R2019a | File I/O | Not Recommended |
strmatch | startsWith / strncmp / matches | R2019b | Strings | Not Recommended |
uicontrol | uibutton / uidropdown / etc. | R2016a | UI/App | Not Recommended |
guide | appdesigner | R2025a | UI/App | Removed |
hgexport('readstyle') | exportgraphics | R2025a | Graphics | Removed |
nargin / nargout in scripts | convert the script to a function | R2022a | Language | Removed |
str2num | str2num(text, Evaluation="restricted") | R2022a | Security | Unrestricted form runs input via eval |
eval / evalc / evalin | Dynamic field names, function handles / feval, cells for sequential names | — | Anti-pattern | Not compiled (slow); overwrites workspace vars; hard to debug; injection risk |
containers.Map | dictionary | R2022b | Containers | Not Recommended — not flagged by Code Analyzer |
bsxfun | Implicit expansion | R2016b | Arrays | Not Recommended (superseded) |
File I/O Modernization
csvread, dlmread → readmatrix
Status: Not recommended as of R2019a
Old Pattern (Avoid):
M = csvread('data.csv');
M = dlmread('data.txt', '\t');
M = dlmread('data.csv', ',', 1, 0); % Skip header rowModern Pattern (Use This):
M = readmatrix('data.csv');
M = readmatrix("data.txt", Delimiter="\t");
% Skip header rows
opts = detectImportOptions('data.csv');
opts.DataLines = [2 Inf];
M = readmatrix('data.csv', opts);Why Modern is Better:
Better cross-platform support and performance
Automatic detection of data format and types
Import options for controlling the import process
Better error handling and missing data management
csvwrite, dlmwrite → writematrix
Status: Not recommended as of R2019a
Old Pattern (Avoid):
csvwrite('output.csv', M);
dlmwrite('output.txt', M, '\t');
dlmwrite('output.csv', M, '-append');Modern Pattern (Use This):
writematrix(M, 'output.csv');
writematrix(M, "output.txt", Delimiter="\t");
writematrix(M, "output.csv", WriteMode="append");xlsread → readtable, readmatrix, readcell
Status: Not recommended as of R2019a
Old Pattern (Avoid):
[num, txt, raw] = xlsread('data.xlsx');
data = xlsread('data.xlsx', 'Sheet1', 'A2:D100');Modern Pattern (Use This):
% For tabular data (most common)
T = readtable('data.xlsx');
T = readtable("data.xlsx", Sheet="Sheet1", Range="A2:D100");
% For numeric matrix
M = readmatrix('data.xlsx');
% For cell array (mixed types)
C = readcell('data.xlsx');Why Modern is Better:
readtablepreserves variable names and typesBetter handling of mixed data types
Works consistently across platforms
Better datetime and categorical support
xlswrite → writetable, writematrix, writecell
Status: Not recommended as of R2019a
Old Pattern (Avoid):
xlswrite('output.xlsx', data);
xlswrite('output.xlsx', data, 'Sheet1', 'A2');Modern Pattern (Use This):
% For tables
writetable(T, 'output.xlsx');
writetable(T, "output.xlsx", Sheet="Sheet1", Range="A2");
% For matrices
writematrix(M, 'output.xlsx');
% For cell arrays
writecell(C, 'output.xlsx');String Function Modernization
strmatch → startsWith, strncmp, matches
Status: Not recommended
Old Pattern (Avoid):
idx = strmatch('abc', strArray);
idx = strmatch('abc', strArray, 'exact');Modern Pattern (Use This):
% Find strings starting with pattern
idx = find(startsWith(strArray, 'abc'));
% Find exact matches
idx = find(matches(strArray, 'abc'));
% Case-insensitive
idx = find(startsWith(strArray, "abc", IgnoreCase=true));Character Arrays → String Arrays
Recommendation: Use string arrays for new code
Old Pattern (Less Preferred):
name = 'John';
names = {'John', 'Jane', 'Bob'};
fullName = [firstName, ' ', lastName];Modern Pattern (Preferred):
name = "John";
names = ["John", "Jane", "Bob"];
fullName = firstName + " " + lastName;Why Modern is Better:
String arrays work better with modern MATLAB functions
Easier concatenation with
+operatorBetter missing value handling with
<missing>More intuitive indexing
strfind → contains (for string arrays)
Recommendation: Use contains for string array searches
Old Pattern:
idx = ~cellfun('isempty', strfind(cellstr, pattern));Modern Pattern:
idx = contains(stringArray, pattern);UI/App Development Modernization
uicontrol → Modern UI Components
Status: Not recommended. Use App Designer components.
Old Pattern (Avoid):
fig = figure;
btn = uicontrol('Style', 'pushbutton', 'String', 'Click Me', ...
'Position', [20 20 100 30], 'Callback', @buttonCallback);
edit = uicontrol('Style', 'edit', 'Position', [20 60 100 25]);
popup = uicontrol('Style', 'popupmenu', 'String', {'A','B','C'});Modern Pattern (Use This):
fig = uifigure;
btn = uibutton(fig, 'Text', 'Click Me', ...
'Position', [20 20 100 30], 'ButtonPushedFcn', @buttonCallback);
edit = uieditfield(fig, 'Position', [20 60 100 25]);
dropdown = uidropdown(fig, 'Items', {'A','B','C'});UI Component Mapping:
| uicontrol Style | Modern Component |
|---|---|
| pushbutton | uibutton |
| edit | uieditfield |
| text | uilabel |
| popupmenu | uidropdown |
| listbox | uilistbox |
| checkbox | uicheckbox |
| radiobutton | uiradiobutton |
| slider | uislider |
| togglebutton | uibutton (with state) |
Why Modern is Better:
Modern appearance consistent with App Designer
Better styling and customization options
Responsive layouts with uigridlayout
Better touch/mobile support
guide → appdesigner
Status: Removed in R2025a (discouraged since R2021a).
GUIDE (Graphical User Interface Development Environment) was MATLAB's original drag-and-drop app
builder. The interactive environment has been removed and replaced by App Designer. guide now
resolves to a stub that errors when called (identifier MATLAB:guide:GUIDEHasBeenRemoved).
Old Pattern (Will Error):
% REMOVED (R2025a): the GUIDE environment no longer exists
guide % Errors: GUIDE has been removed
guide('myapp.fig') % Errors: GUIDE has been removedModern Pattern (Use This):
appdesigner % Create a new app
appdesigner('myapp.mlapp') % Open an existing App Designer app
app = myapp; % Run an app programmaticallyMigration:
Use the GUIDE to App Designer Migration Tool (App Designer → Designer tab → Open → Migration tool) to convert existing
.fig+.mapps.Existing GUIDE apps can still run in R2025a+ (
.fig+.m); you just can't edit them in the removed GUIDE environment. Migrate for maintainability.
Callback pattern change (handles struct → app properties):
% OLD GUIDE callback (will not work interactively)
function pushbutton1_Callback(hObject, eventdata, handles)
handles.data = handles.data + 1;
guidata(hObject, handles);
set(handles.text1, 'String', num2str(handles.data));
end
% NEW App Designer callback
function Button1Pushed(app, event)
app.Data = app.Data + 1;
app.Label1.Text = num2str(app.Data);
endKey differences: single .mlapp file (not .fig + .m); callbacks live with the layout; data
sharing via app properties (app.PropertyName) instead of the handles struct and guidata.
hgexport('readstyle') → exportgraphics
Status: Removed in R2025a.
The hgexport('readstyle', ...) function for reading export style files has been removed.
Old Pattern (Will Error):
% REMOVED (R2025a):
style = hgexport('readstyle', 'mystyle.txt');
hgexport(gcf, 'output.eps', style);Modern Pattern (Use This):
exportgraphics(gcf, "output.pdf", ContentType="vector");
exportgraphics(gcf, "output.png", Resolution=300);
exportgraphics(gca, "output.eps", ContentType="vector");
% For more control, set figure/axes properties directly, then export
fig = gcf;
fig.Color = "white";
ax = gca;
ax.FontSize = 12;
exportgraphics(fig, "output.pdf");exportgraphics needs no external style files, works with axes/figures/chart containers, and gives
better PDF and vector output.
Security Recommendations
str2num → str2num with restricted evaluation
Recommendation: Replace str2num(text) with str2num(text, Evaluation="restricted")
(R2022a+). Restricted evaluation accepts only numeric and basic math expressions, so injected code
cannot execute. This is the secure, drop-in replacement.
Old Pattern (Security Risk):
% DANGEROUS: unrestricted str2num evaluates its input with eval, so a string
% like "system('rm -rf /')" would run.
value = str2num(userInput);Modern Pattern (Secure):
% SECURE: restricted evaluation accepts only numeric/math expressions.
value = str2num(userInput, Evaluation="restricted");Why This Matters:
Unrestricted
str2numevaluates its input withevaland can run arbitrary code.Evaluation="restricted"confines evaluation to numeric and math expressions, so injected function calls cannot execute — closing the injection risk.
eval → Structured Alternatives
Recommendation: Avoid eval, evalc, and evalin. Per MathWorks best practice
(Alternatives to the eval Function),
eval code:
Is not compiled. MATLAB compiles code on first run to speed up later runs, but because
evaltext can change at run time it cannot be compiled — so it is slower.Can silently overwrite workspace variables.
evalmay create or assign to a variable already in the current workspace, clobbering existing data.Is hard to read and debug, and (for
eval/str2numon external input) opens a code-injection risk. The Code Analyzer cannot see through the string, so it can't help.
Almost every eval use maps to a clearer construct. Match the pattern, don't reach for eval:
1. Sequentially named variables (A1, A2, …) → one array / cell / struct
% Old:
% for n = 1:10
% eval(['A' int2str(n) ' = magic(n);']);
% end
numArrays = 10;
A = cell(numArrays,1);
for n = 1:numArrays
A{n} = magic(n); % index instead of building a name
end2. Sequentially named files → build the name with sprintf, call in function syntax
% Old: eval(['save myfile' int2str(n) '.mat randomData']);
for n = 1:10
randomData = rand(n);
currentFile = sprintf('myfile%d.mat', n); % sprintf beats int2str
save(currentFile, 'randomData')
end3. Function name held in a variable → function handle or feval
% Old: result = eval(['process_' methodName '(x)']);
% Preferred: map names to handles (dispatch table)
methodHandles = struct("fast", @processFast, "slow", @processSlow);
result = methodHandles.(methodName)(x);
% Or convert a name to a handle
h = str2func("process_" + methodName);
result = h(x);
% Or, when the name arrives at run time (e.g. user input), feval
result = feval(methodName, x);feval is a recommended alternative here, not itself an anti-pattern.
4. Dynamic struct field → parenthesized field name
% Old: eval(['s.' fieldName ' = 42;']);
s.(fieldName) = 42; % assign
dataToUse = myData.(fieldName); % read5. eval used as an implicit try/catch → explicit try/catch
% Old (implicit catch): eval("B = A;", "disp('A is undefined')")
try
B = A;
catch exception
disp("A is undefined")
endThe older eval(expression, catch_expr) form is not recommended — an explicit try/catch is
significantly clearer.
6. evalc('disp(x)') to capture a variable's display text → formattedDisplayText
A common evalc use is capturing what disp would print — for a log line, an error message, or a
report — by running disp for its side effect and grabbing the command-window output. evalc runs
the string uncompiled and is opaque to the Code Analyzer. Since R2021a, formattedDisplayText
returns that same text directly, as a string.
% Old: run disp for its side effect, capture the command-window text
txt = evalc('disp(x)');
% Modern: get the formatted display text directly (returns a string)
txt = formattedDisplayText(x);The two are identical except that evalc appends one extra trailing newline (from the command-window
echo) that formattedDisplayText omits — usually what you want. Because it returns a string, it
composes directly:
A = [1 2 3; 4 5 6];
msg = "Matrix A =" + newline + formattedDisplayText(A);formattedDisplayText also takes name-value options for full control over the rendering — e.g.
NumericFormat="longG", LineSpacing="compact", SuppressMarkup=true — which the evalc('disp(...)')
form cannot express.
When you truly need a run-time key-value store (not a fixed set of fields), reach for a
dictionary (R2022b+, preferred over containers.Map) rather than synthesizing variable names —
see the Map Container section below.
Map Container Modernization
containers.Map → dictionary
Status: dictionary (R2022b+) is the preferred key-value type. containers.Map is a legacy
handle class and still works, but dictionary is a value type, is faster, supports vectorized
lookup, and integrates with the modern data types. Migrate new code to dictionary.
Old Pattern (Legacy):
m = containers.Map({'alpha','beta','gamma'}, {1.5, 2.0, 0.75});
s = m('beta'); % -> 2.0
m('delta') = 3.0; % add
tf = isKey(m, 'alpha'); % membership
remove(m, 'gamma'); % delete (mutates in place — handle semantics)
n = m.Count; % number of entries
allKeys = keys(m); % cell array of keysModern Pattern (Use This):
d = dictionary(["alpha" "beta" "gamma"], [1.5 2.0 0.75]);
s = d("beta"); % -> 2.0
d("delta") = 3.0; % add
tf = isKey(d, "alpha"); % membership
d = remove(d, "gamma"); % delete — VALUE semantics, so REASSIGN d
n = numEntries(d); % number of entries (not .Count)
allKeys = keys(d); % string array of keys (not a cell array)Key differences when migrating — these bite if you swap the name blindly:
| Concern | containers.Map | dictionary |
|---|---|---|
| Semantics | handle — remove(m,k) mutates in place | value — d = remove(d,k) must be reassigned |
| Construct | containers.Map(keys, vals) (cell arrays) | dictionary(keys, vals) (arrays; string keys, not char) |
| Count | m.Count | numEntries(d) |
| Keys/values | keys(m)/values(m) → cell arrays | keys(d)/values(d) → typed arrays |
| Missing key | errors (MATLAB:Containers:Map:NoKey) | errors (MATLAB:dictionary:ScalarKeyNotFound) — use lookup(d, k, FallbackValue=...) for a default |
| Key type | 'char' keys | prefer "string" keys; numeric keys also supported |
Incremental build (empty then fill):
% Old
m = containers.Map('KeyType','char','ValueType','double');
m('x') = 10;
% Modern, fill-immediately — start empty, key/value types are inferred on first insert
d = dictionary;
d("x") = 10;
% Modern, query-before-insert — configure the types up front so isKey/lookup
% work while the dictionary is still empty (the registry / cache / singleton shape)
d = configureDictionary("string","double");
tf = isKey(d, "x"); % -> false, no error
d("x") = 10;Critical for stateful maps: a bare dictionary is unconfigured until its first insert. Calling
lookup, isKey, or indexing it before then errors with
MATLAB:dictionary:UnconfiguredLookupNotSupported. This is the single most likely failure when
migrating a registry or cache class that queries the map before it is populated — use
configureDictionary(keyType, valueType) instead of bare dictionary. Guard any dictionary that may
be unconfigured with isConfigured(d) before iterating keys/values.
Values that are not scalars (vectors, mixed types) — use a cell-valued dictionary, same as
containers.Map's cell values:
d = dictionary(["u" "v"], {[1 2 3], "hello"});
row = d("u"); % row is a 1x1 cell
row = row{1}; % -> [1 2 3]ValueType='any' (heterogeneous values) — the migration that bites hardest. A
containers.Map('KeyType','char','ValueType','any') stores values of mixed class per key
(scalars, vectors, strings, structs). dictionary has no 'any' value type — a dictionary's values
share one type. The correct target is a cell-valued dictionary: configure it as "cell" and use
brace indexing (d{"k"}), which auto-wraps on assign and auto-unwraps on read.
% Old — heterogeneous value store
m = containers.Map('KeyType','char','ValueType','any');
m('scalar') = 42;
m('vec') = [1 2 3];
m('cfg') = struct('a',1);
% Modern — cell value type; brace indexing auto-wraps/unwraps
d = configureDictionary("string","cell");
d{"scalar"} = 42;
d{"vec"} = [1 2 3];
d{"cfg"} = struct('a',1);
v = d{"vec"}; % -> [1 2 3] (brace read unwraps the cell — no temp needed)Use brace indexing (
d{"k"}). Brace assignment wraps the value in the cell for you and brace read unwraps it, so the call sites read exactly like the oldcontainers.Map(m(k) = v→d{k} = v) — no manual{...}wrapping. This is the cleanest target for an'any'map.Paren indexing still exposes the raw cell.
d("k")returns the 1x1 cell (v = d("k"); v = v{1}to unwrap);d{"k"}returns the value directly. Prefer braces unless you deliberately want the cell.d("k"){1}does not parse —{}directly after()is disallowed (same rule as chained()above). Brace-indexing the dictionary itself (d{"k"}) sidesteps this — no temp variable needed.Don't reach for
"cell"when the values are actually homogeneous. ManyValueType='any'maps hold one type in practice (all doubles, all strings). If so, migrate to the natural typed dictionary (configureDictionary("string","double")) and skip the cell wrapping entirely — inspect the real values before choosing. Reserve the cell-valued form for genuinely mixed-type stores.
Iterate over entries:
% Old
for k = keys(m) % k is a 1x1 cell
disp(m(k{1}))
end
% Modern — keys(d) is a string array; transpose to loop columns
for k = keys(d)'
disp(d(k))
endWatch-outs:
removereturns the updated dictionary —remove(d,k)without reassignment does nothing visible, becausedictionaryis a value type.containers.Mapmutated in place;dictionarydoes not. This is the single most common migration bug.Don't index a lookup result with chained parentheses —
d(k)(1)is illegal in R2026a (Using parentheses '()' directly after parentheses '()' is disallowed). Use a temp:v = d(k); v(1).The missing-key error identifier changes.
containers.MapthrowsMATLAB:Containers:Map:NoKey;dictionarythrowsMATLAB:dictionary:ScalarKeyNotFound. Anycatchblock that testse.identifieragainst the Map id must be updated — otherwise it silently rethrows instead of handling the missing key.Keys are best kept as string (
"beta"). char ('beta') is not supported as a key. If passed a char,dictionarycoerces it to a string.
Superseded Vectorization Idioms
bsxfun → implicit expansion
Status: bsxfun is superseded — avoid it in new code. Since R2016b, MATLAB applies implicit
expansion (broadcasting) to element-wise operators and two-input functions automatically, so the
explicit bsxfun call is legacy and no longer recommended.
Old Pattern (superseded — do not use bsxfun):
normalized = bsxfun(@rdivide, A, max(A)); % legacy: bsxfun is superseded
centered = bsxfun(@minus, A, mean(A)); % legacy: avoid bsxfun here tooModern Pattern (implicit expansion, R2016b+):
normalized = A./max(A); % broadcasts the 1×n max across rows
centered = A - mean(A); % broadcasts the 1×n mean across rowsThis is a modernization swap (a superseded API → its current equivalent), which is why it lives in
this skill. Making an already-correct loop faster by vectorizing it — choosing whether to vectorize
at all, and measuring the speedup — is optimization work owned by matlab-optimize-performance (see
SKILL.md "When NOT to Use").
Language Removals
nargin / nargout in scripts
Status: Support removed in R2022a.
nargin and nargout report the argument counts of a function call. Calling them at script
scope errors in R2022a+.
% OLD — errors in R2022a+ when used in a script
if nargin > 0
% ...
endFix: convert the script to a function so it has a real argument list.
function processData(varargin)
if nargin > 0
% ...
end
endDesign Pattern Modernization
SKILL.md shows the always-loaded base-language patterns (table workflows, string
arrays, arguments blocks) as terse snippets. This
section adds the patterns that don't fit inline.
Tall Arrays for Big Data
For data that doesn't fit in memory:
% Modern: Use tall arrays
ds = datastore('bigdata/*.csv');
T = tall(ds);
result = gather(mean(T.Value)); % Processes in chunksCopyright 2026 The MathWorks, Inc.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- MathWorks
- Publisher
- MathWorks (Official)
- Source
- matlab/matlab-agentic-toolkit · skills-catalog/matlab-software-development/matlab-modernize-code · commit 2a7f215
- Licence
- BSD 2-Clause License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE.md)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by MathWorks or MathWorks.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download matlab-modernize-code.zip