Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Patience and the right tools – ways to approach...
Search
stefan judis
February 08, 2018
Technology
700
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Patience and the right tools – ways to approach content modelling
stefan judis
February 08, 2018
More Decks by stefan judis
See All by stefan judis
Back to boring (part 2)
stefanjudis
0
390
Playwright can do this?
stefanjudis
0
270
Things you should know about Frontend Development in 2022
stefanjudis
0
570
Throw yourself out there for fun and profit
stefanjudis
0
140
Back to Boring
stefanjudis
1
550
Wanna scale up? Make sure your CMS is ready for it!
stefanjudis
0
290
Did we(b development) lose the right direction?
stefanjudis
6
2.2k
Regular expressions – my secret love
stefanjudis
1
1.1k
Write a Function
stefanjudis
0
640
Other Decks in Technology
See All in Technology
覗いてみよう 関数型ビジュアル言語×2Dグラフィックスの世界
yohyamasaki
0
160
私の推しは「聞いてから進む」AIです -AI-DLCに一人でアプリを作らせた話
yama3133
0
150
Snowflake Horizon Catalog と Apache Iceberg で作る オープンなデータ基盤
kitagawaz
0
360
カンファレンスに参加した後の浮遊感とセルフケア
pauli
0
270
AWS DevOps Agent スキルをつかいこなそう / Master AWS DevOps Agent Skills
kinunori
2
650
React Nativeでの OTA Updateって、 どう説明する?
ichiki1023
0
130
Claude起点の仕様駆動開発
tanakaseiya
0
260
スキルを作る、その前に!複数人で使われるスキルを 作るためのプロセス
junkifurukawa
1
140
AI駆動開発で仕様はどこまで書くべきか? ― 人とAIの責務境界から考える開発プロセスの実践
takahiromatsui
1
270
AIエージェント時代のPlatform as a Product —— テックリードがPdMとして回す発見・導入・計測 / Platform as a Product in the AI Agent Era
toshi0607
1
550
[2026-09-30]ロックンロールは鳴り止まないっ - 信頼性かまってちゃん - 「データ駆動を投げ捨ててまで。」追いかける信頼性改善に向けた取り組みの話
tosite
0
130
Execution in the Kingdom of Agents: Reflections on Abstraction and Complexity
bcantrill
0
610
Featured
See All Featured
Optimizing for Happiness
mojombo
378
71k
BBQ
matthewcrist
89
10k
Designing Powerful Visuals for Engaging Learning
tmiket
1
580
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
2
2.9k
Color Theory Basics | Prateek | Gurzu
gurzu
1
480
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
270
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.4k
The Cost Of JavaScript in 2023
addyosmani
55
10k
Raft: Consensus for Rubyists
vanstee
142
7.7k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
2
2.2k
GraphQLとの向き合い方2022年版
quramy
50
15k
Building a Scalable Design System with Sketch
lauravandoore
464
34k
Transcript
Patience and the right tools @stefanjudis Ways to approach content
modelling
Stefan Judis Frontend Developer, Occasional Teacher, Meetup Organizer ❤ Open
Source, Performance and Accessibility ❤ @stefanjudis
Where we come from
Karen McGrane I don’t necessarily know where this content is
going to live. I don’t know how it’s going to be used in the future. But, I do know that I have to create lots of flexible content that has metadata attached to it because I know it’s going to be reused in different places. “
None
None
It's like Lego
NOBODY(!) GETS IT RIGHT THE FIRST TIME...
share Share & reuse
github.com/joshhebb/angularjs-contentful-starter
None
www.npmjs.com/package/contentful-import www.npmjs.com/package/contentful-export
It's just code Setting up a space is quickly done
When you know how...
I believe in tooling and defaults!
Eat your own dog food
None
USEFUL FOR ANYONE?
Share content models?
None
www.contentfulcommunity.com MAYBE HERE?
Analyse
None
www.npmjs.com/package/contentful-graph
None
YOUR CONTENT MODEL VISUALISED.
None
None
CLI is great, but there is more
contentful-graph.yaraslav.com/
Evolve
Clean up time
www.npmjs.com/package/contentful-migration-cli
MIGRATION CLI CONTENT TYPE OPERATIONS Create a content type 01
02 03 04 05 Delete a content type Edit a content type Create/edit/delete fields Change a field ID
FIELDS TO DELETE
Drop fields module.exports = function (migration) { const event =
migration.editContentType('event') const project = migration.editContentType('project') }
module.exports = function (migration) { const event = migration.editContentType('event') event.deleteField('hotel')
event.deleteField('venue') const project = migration.editContentType('project') project.deleteField('screenshot') } Drop fields
None
FIELDS TO RENAME VS
module.exports = function (migration) { const tilPost = migration.editContentType('tilPost') }
Change field ID
module.exports = function (migration) { const tilPost = migration.editContentType('tilPost') tilPost.changeFieldId('categories',
'tags') tilPost.editField('tags', { name: 'Tags' }) tilPost.moveField('tags').afterField('date') } Change field ID
None
FRONTTRENDS 2017 COUNTRY : PL CITY : WARSAW ... FRONTTRENDS
2018 COUNTRY : PL CITY : WARSAW ... DERIVE ENTRIES AND LINK
FT COUNTRY: PL CITY : WARSAW FRONTTRENDS 2017 COUNTRY :
PL CITY : WARSAW ... FRONTTRENDS 2018 COUNTRY : PL CITY : WARSAW ... DERIVE ENTRIES AND LINK
FRONTTRENDS 2017 CONFERENCE : FT ... FRONTTRENDS 2018 CONFERENCE :
FT ... FT COUNTRY: PL CITY : WARSAW DERIVE ENTRIES AND LINK
A looong manual nightshift
No way!
repetetive error-prone not scalable MANUAL CONTENT MODEL CHANGES ARE NOT
THE SOLUTION
Transform an entry in place 01 02 Derive an entry
from another MIGRATION CLI CONTENT TRANSFORMATIONS
module.exports = function (migration) { const conference = migration.createContentType('conference') .name('Conference/Meetup')
.displayField('name') conference.createField('name') .type('Symbol') .required(true) .name('Conference/Meetup name') conference.createField('country') .type('Symbol') .required(true) .name('Country Code') conference.createField('city') .type('Symbol') .required(true) .name('City') Derive entries and link
.name('Country Code') conference.createField('city') .type('Symbol') .required(true) .name('City') const event = migration.editContentType('event')
event.createField('conference') .name('Conference') .type('Link') .linkType('Entry') .validations([ { "linkContentType": ['conference'] } ]) Derive entries and link
]) migration.deriveLinkedEntries({ contentType: 'event', from: ['name', 'country', 'city'], toReferenceField: 'conference',
derivedContentType: 'conference', derivedFields: ['name', 'country', 'city'], identityKey: async (from) => { return getId(from.name['en-US']) }, deriveEntryForLocale: async (inputFields, locale) => { return { name: inputFields.name[locale].replace(/\s(\d{2,4}|#\d+)/g, ''), country: inputFields.country[locale] || 'N/A', city: inputFields.city[locale] || 'N/A' } } }) Derive entries and link
]) migration.deriveLinkedEntries({ contentType: 'event', from: ['name', 'country', 'city'], toReferenceField: 'conference',
derivedContentType: 'conference', derivedFields: ['name', 'country', 'city'], identityKey: async (from) => { return getId(from.name['en-US']) }, deriveEntryForLocale: async (inputFields, locale) => { return { name: inputFields.name[locale].replace(/\s(\d{2,4}|#\d+)/g, ''), country: inputFields.country[locale] || 'N/A', city: inputFields.city[locale] || 'N/A' } } }) Derive entries and link
]) migration.deriveLinkedEntries({ contentType: 'event', from: ['name', 'country', 'city'], toReferenceField: 'conference',
derivedContentType: 'conference', derivedFields: ['name', 'country', 'city'], identityKey: async (from) => { return getId(from.name['en-US']) }, deriveEntryForLocale: async (inputFields, locale) => { return { name: inputFields.name[locale].replace(/\s(\d{2,4}|#\d+)/g, ''), country: inputFields.country[locale] || 'N/A', city: inputFields.city[locale] || 'N/A' } } }) Derive entries and link
]) migration.deriveLinkedEntries({ contentType: 'event', from: ['name', 'country', 'city'], toReferenceField: 'conference',
derivedContentType: 'conference', derivedFields: ['name', 'country', 'city'], identityKey: async (from) => { return getId(from.name['en-US']) }, deriveEntryForLocale: async (inputFields, locale) => { return { name: inputFields.name[locale].replace(/\s(\d{2,4}|#\d+)/g, ''), country: inputFields.country[locale] || 'N/A', city: inputFields.city[locale] || 'N/A' } } }) Derive entries and link
} } }) event.moveField('conference').afterField('name') event.deleteField('country') event.deleteField('city') } Derive entries and
link
None
module.exports = function (migration) { const conference = migration.createContentType('conference') .name('Conference/Meetup')
.displayField('name') conference.createField('name').type('Symbol').required(true).name('Conference/Meetup name') conference.createField('country').type('Symbol').required(true).name('Country Code') conference.createField('city').type('Symbol').required(true).name('City') const event = migration.editContentType('event') event.createField('conference') .name('Conference') .type('Link') .linkType('Entry') .validations([ { "linkContentType": ['conference'] } ]) migration.deriveLinkedEntries({ contentType: 'event', from: ['name', 'country', 'city'], toReferenceField: 'conference', derivedContentType: 'conference', derivedFields: ['name', 'country', 'city'], identityKey: async (from) => { return from.name['en-US'] // remove year .replace(/\s(\d{2,4}|#\d+)/g, '') // clear spaces .replace(/\s/g, '-') // clear "weird characters" .replace(/(,|\/|\\|:|\.|\(|\))/g, '') .toLowerCase() }, deriveEntryForLocale: async (inputFields, locale) => { return { name: inputFields.name[locale].replace(/\s(\d{2,4}|#\d+)/g, ''), country: inputFields.country[locale] || 'N/A', city: inputFields.city[locale] || 'N/A' } } }) event.moveField('conference').afterField('name') event.deleteField('country') event.deleteField('city') } LOC 48 REQUESTS 382 ENTRIES CREATED 88 ENTRIES UPDATED 96
MIGRATION CLI ADVANTAGES Repeatable 01 02 03 04 Can be
kept in VC Includes sanity checks Perfect for CI
NOBODY(!) GETS IT RIGHT THE FIRST TIME...
61 WAYS TO SURVIVE SHARE & REUSE ANALYSE EVOLVE
with the right tools...
Thanks. @stefanjudis Slides ctfl.io/content-modelling-is-tricky Article ctfl.io/content-modelling-is-tricky-article