Insights on what 10 years of trying to go global has taught us.
Presented during #21st Athens Ruby Meetup from team e-Travel (mytrip.com, airtickets24.com, trip.ru, trip.ae, pamediakopes.gr, et al).
2. Hello!
We are team e-Travel
We are here to share what 10 years of trying
to go global has taught us
3. Tasos Latsas
@tlatsas
1+ years in e-Travel:
◦ ~1 year doing ruby/web development
◦ ~2 years doing python/web development
◦ ~6 years doing random stuff with linux
systems/services/distros
4. Nikos Dimitrakopoulos
@nikosd
6 years in e-Travel & Fraudpointer:
◦ ~1 year C#/web development
◦ ~4 years ruby/web development
◦ For the last year “disarmed” from coding,
leading the Product team
Ruby fanboy since 2004 :)
7. Long time ago… (2004 - 2008)
That at some point became two sites
With multiple brandings and multiple languages
There was a simple ASP.Net site...
8. Long time ago… (2004 - 2008)
But they were actually different sites
◦ Using the same codebase
◦ But different deployments
9. Long time ago… (2004 - 2008)
Each site was hardwired to a specific
branding, language and “market”
10. Long time ago… (2004 - 2008)
◦ For example pamediakopes.gr
▫ Was in Greek
▫ With “pamediakopes.gr” branding
▫ And was targeted to the Greek and
Cyprus markets
11. Long time ago… (2004 - 2008)
◦ For example pamediakopes.gr
▫ Was in Greek
▫ With “pamediakopes.gr” branding
▫ And was targeted to the Greek and
Cyprus markets
◦ And fantasticgreece.com/de
▫ Was in German
▫ With “fantasticgreece.com” branding
▫ And was targeted to the German
market
14. Medieval ages (2008 - 2011)
Translations automation & management
▫ Scripts & tools for extraction of keys
(Gettext)
▫ Standardized po files (Gettext) as
translation dictionaries
▫ Transifex to the rescue as a
management platform!
15. Medieval ages (2008 - 2011)
Each new market was a major project (year+)
◦ Either as a new sub-site (for example
“airtickets24.com/ru”)
◦ Or as a brand new, stand-alone domain
(for example trip.ru)
16. Medieval ages (2008 - 2011)
l10n and i18n still an afterthought and mostly
just translations
17. Medieval ages (2008 - 2011)
At the same time complexity exploded
▫ New products
▫ Smarter products
▫ More features
▫ New platforms
▫ First “APIs”
18. Medieval ages (2008 - 2011)
Almost everything in big fat “solutions”
▫ Business logic
▫ Presentation
▫ Persistence
▫ i18n
▫ ...
20. Industrial revolution (2008 - 2011)
Jumping into the micro-services wagon
(before the term even existed - we called it
then “SOA” without the fluff)
21. Industrial revolution (2008 - 2011)
◦ Break pieces into REST services
◦ Build robust and modern client front-ends
◦ Ruby + Rails come into play
22. Industrial revolution (2008 - 2011)
We started building a Rails app as the web
front-end with:
◦ Modern web practices
◦ Horizontal scalability
◦ Automated & smooth deployment
◦ Extensive test suite
◦ i18n built-in
25. Industrial revolution (2008 - 2011)
i18n built-in: whack a mole
◦ rudimentary support from rails for full
blown gettext (plurals, interpolations, keys
extraction, po backend)
26. Industrial revolution (2008 - 2011)
i18n built-in: whack a mole
◦ rudimentary support from rails for full
blown gettext (plurals, interpolations, keys
extraction, po backend)
◦ again, rudimentary support time formats
(15 Ιανουάριος)
27. Industrial revolution (2008 - 2011)
i18n built-in: whack a mole
◦ rudimentary support from rails for full
blown gettext (plurals, interpolations, keys
extraction, po backend)
◦ again, rudimentary support time formats
(15 Ιανουάριος)
◦ fallbacks working only as proof of concept
(:de_DE -> :de -> :en)
28. Industrial revolution (2008 - 2011)
i18n built-in: number_to_currency
◦ Is slooooooooow
◦ Makes bad assumptions
▫ currency is determined based on locale
▫ reaaaally?
29. Industrial revolution (2008 - 2011)
[1] pry(main)> i = 100.10
=> 100.1
[2] pry(main)> Benchmark.bmbm do |x|
[2] pry(main)* x.report('printf') { 1000.times { '%.2f' % i } }
[2] pry(main)* x.report('number_to_currency') { 1000.times { helper.number_to_currency(i) }
}
[2] pry(main)* end
Rehearsal ------------------------------------------------------
printf 0.000000 0.000000 0.000000 ( 0.004235)
number_to_currency 1.370000 0.070000 1.440000 ( 1.492025)
--------------------------------------------- total: 1.440000sec
user system total real
printf 0.000000 0.000000 0.000000 ( 0.001912)
number_to_currency 0.150000 0.000000 0.150000 ( 0.149475)
30. Industrial revolution (2008 - 2011)
i18n built-in: performance/memory issues
◦ 4s to read the po files in memory (!) for
“just” 8 languages
31. Industrial revolution (2008 - 2011)
i18n built-in: performance/memory issues
◦ 4s to read the po files in memory (!) for
“just” 8 languages
◦ Solution: “compile” them to ruby code (!)
32. Industrial revolution (2008 - 2011)
i18n built-in: performance/memory issues
◦ 4s to read the po files in memory (!) for
“just” 8 languages
◦ Solution: “compile” them to ruby code (!)
▫ < 1s to load on startup
▫ but bloating the memory (> 40mb /
process)
37. Industrial revolution (2008 - 2011)
UI/UX
◦ different languages → different space
requirements on the screen
◦ different font requirements (e.g. arabic,
thai)
◦ different font size requirements
◦ RTL (lol good luck)
40. Industrial revolution (2008 - 2011)
Translations management
◦ still <3 transifex
◦ still <3 Gettext parser
◦ (new) homebrewed bunch of scripts
syncing with transifex and
committing to repo
43. Industrial revolution (2008 - 2011)
1st take of automated QA for
translations:
→ smoke tests
◦ … a lot of them …
◦ … 3 hours to run …
◦ but saved a lot of releases
44. Industrial revolution (2008 - 2011)
Apart from the main Rails Web app, we
started building another big Rails app as the
CRM back-end
45. Industrial revolution (2008 - 2011)
And a whole zoo of standalone services
serving content & business logic in the middle
47. Industrial revolution (2008 - 2011)
Launching a new market was still a major
project
~ definitely less than a year
~ much more streamlined
~ 2 new markets per year
but still a big and dodgy project
49. Industrial revolution (2008 - 2011)
◦ Logic was still hard-coded
◦ Macro complexity has increased even
though micro complexity had decreased
◦ Sync different teams, with different
codebases, different apps, even different
technologies
54. ~ 1,800,000 users
(per month)
with 400 locales
(“el-GR”, “ru”, “en-US”, etc)
from 234 countries
(Including names like “Djibouti”, “Belize”, etc)
55. 7+ different “platforms”
(Web, iOS, Android, SMS, emails, telephone,
push notifications, more to come?)
40+ releases / week
(0 downtime… mostly)
Tens of services
(running on C# and Ruby)
56.
57. Time To Go Live
◦ Company level time to go live: ~ 4 weeks
▫ translations
▫ configurations
▫ release
◦ Dev level time to go live: couple of days
58. Our approach
One codebase (per app) supporting
different configurations
vs
multiple different deployments
62. Our approach
Introduce a new (configuration) service
◦ Share configurations to multiple services
63. Our approach
Introduce a new (configuration) service
◦ Share configurations to multiple services
◦ Separate deploy schedules
64. Our approach
Introduce a new (configuration) service
◦ Share configurations to multiple services
◦ Separate deploy schedules
◦ Centralized configuration logic
65. Our approach
Introduce a new (configuration) service
◦ Share configurations to multiple services
◦ Separate deploy schedules
◦ Centralized configuration logic
◦ RIP mighty “language selector” xD
66. Configuration service
◦ Built with ruby
◦ Nginx + AWS S3
◦ Keep It Simple, Stupid™
▫ Read json files
▫ Process
▫ Permutate
▫ Output json configuration(s)
▫ Upload to Amazon S3 bucket (easy
deployment + free .9999 reliability)
68. Configuration service clients
◦ Query the service for settings using any
brand/country/language combination
◦ Clients do not care and do not make
assumptions (when you assume you make an ass out of u and me)
69. Configuration service clients
◦ Query the service for settings using any
brand/country/language combination
◦ Clients do not care and do not make
assumptions (when you assume you make an ass out of u and me)
◦ Get all available info for the combination
they asked for
70. Configuration service clients
◦ Query the service for settings using any
brand/country/language combination
◦ Clients do not care and do not make
assumptions (when you assume you make an ass out of u and me)
◦ Get all available info for the combination
they asked for
◦ Can get extra info on demand (e.g.
validation rules, legacy market mappings)
71. Configuration service challenges
◦ Micro-services → update tenths of
applications to read from configuration
service (code + tests + deploy)
72. Configuration service challenges
◦ Micro-services → update tenths of
applications to read from configuration
service (code + tests + deploy)
◦ Legacy systems
73. Configuration service challenges
◦ Micro-services → update tenths of
applications to read from configuration
service (code + tests + deploy)
◦ Legacy systems
◦ Caching / performance / availability
74. Configuration service challenges
◦ Micro-services → update tenths of
applications to read from configuration
service (code + tests + deploy)
◦ Legacy systems
◦ Caching / performance / availability
◦ Some of your data becomes irrelevant →
migration tasks
75. Currencies!
UX (and not only) sophistication for
currencies
◦ symbols
◦ delimiters
◦ precisions (!!!!!)
◦ roundings (!!!!!!!!!!!)
77. Turbo-charged automated QA for translations
2nd take of automated QA for
translations:
→ translations checker:
▫ homebrewed build scripts that check for
■ errors (missing/wrong interpolations)
■ warnings (duplicate
keys/lines/interpolations etc)
▫ run in CI after each commit
▫ run in seconds
▫ have paid off again and again and again
84. “
Simferopol is a city on the Crimean
peninsula, the status of which is
disputed between Ukraine and
Russia. It is the administrative
centre of the Autonomous Republic
of Crimea or of the Republic of
Crimea.
(from Wikipedia)
85. Simferopol
Is in Ukraine for Ukrainians
Is “autonomous” for a lot of others
Is in Russia for Russians
86.
87. “
Kosovo is a partially recognised
state in Southeastern Europe that
declared its independence from
Serbia in February 2008 as the
Republic of Kosovo.
(from Wikipedia)
90. Localized business logic
What’s the best sorting in
autocomplete suggestions for
query “PAR” between Paris, Paros &
Parma for:
- Someone from Greece?
- Someone from Italy?
- Someone from France?