XSCREENSAVER / 3D PIPES

[readonly] markdown buffer

Apex Multiline Strings and Interpolation in Summer '26

May 16, 2026 · 6 min read

Summer '26 adds multiline strings and named interpolation through String.template(). It is a modest language change with an immediate benefit: the source can finally resemble the text Apex produces.

Here are the places it helps most, followed by the one place it can make a bad idea look deceptively tidy.

Five useful applications

1. JSON envelopes

Multiline strings make a fixed payload shape easy to review. Serialise every inserted value so quotes and control characters cannot break the JSON.

String payload = '''
{
  "accountId": ${accountId},
  "accountName": ${accountName}
}
'''.template(new Map<String, Object>{
    'accountId' => JSON.serialize(accountId),
    'accountName' => JSON.serialize(accountName)
});

If the whole structure is dynamic, JSON.serialize() on a DTO or map is still simpler and safer. Use a text template when the surrounding document matters.

2. Email bodies

The message now reads like the message rather than a chain of strings and newline escapes.

String body = '''
Hi ${firstName},

Your case ${caseNumber} has been updated.
New status: ${status}

Thanks,
Support
'''.template(new Map<String, Object>{
    'firstName' => firstName,
    'caseNumber' => caseNumber,
    'status' => status
});

For HTML, escape untrusted values for HTML or use a proper email template. Interpolation knows nothing about the output format.

3. Test fixtures

Exact request bodies, responses and error messages are easier to understand when written as blocks.

String expected = '''
Account sync failed
Account: Edge Communications
Reason: Missing external ID
''';

Assert.areEqual(expected, actualMessage);

Whitespace is part of the value, so only assert it exactly when it is part of the behaviour.

4. Operational errors

A diagnostic can include the record, response code and useful context without hiding the intended output among concatenation operators.

Do not confuse readability with permission to log everything. Tokens, session IDs, personal data and full sensitive payloads still do not belong in exceptions or logs.

5. Generated summaries

Task descriptions, internal notes and integration summaries are natural fits. Named placeholders also age better than the positional indexes used by String.format() because ${accountName} explains itself where it appears.

Large, reusable or admin-owned templates should still live in metadata or a template system. This feature is for readable code, not for moving a content-management system into Apex.

The dynamic SOQL trap

This is readable and unsafe:

String query = '''
SELECT Id, Name
FROM Account
WHERE Name LIKE '%${searchTerm}%'
'''.template(new Map<String, Object>{
    'searchTerm' => searchTerm
});

String.template() is not a sanitizer. Keep values in bind variables:

String searchPattern = '%' + searchTerm + '%';

String query = '''
SELECT Id, Name
FROM Account
WHERE Name LIKE :searchPattern
WITH USER_MODE
ORDER BY Name
LIMIT 50
''';

List<Account> accounts = Database.query(query);

Field names and sort directions cannot be bound as values. If those must vary, map the request to a small allowlist chosen by your code before interpolating it.

Map<String, String> allowedFields = new Map<String, String>{
    'name' => 'Name',
    'created' => 'CreatedDate'
};

String sortField = allowedFields.get(sortKey);
if (sortField == null) {
    throw new IllegalArgumentException('Unsupported sort field');
}

The distinction is simple: bind data; allowlist syntax.

Readability, not magic

Multiline strings improve JSON envelopes, emails, tests, errors and generated text because the code shows the intended result. That makes reviews faster and maintenance less brittle.

They do not escape JSON, HTML or SOQL for you. Before inserting a value, ask which language you are generating and handle that context correctly. Cleaner syntax is valuable, but it does not change the security rules underneath it.

Sources