SlideShare a Scribd company logo
Max Voloshin - "Organization of frontend development for products with microservices"
Max Voloshin
● Location: Dnipro, Ukraine
● Position: Team Lead, OWOX
● In professional software development since 2010
● Fan of microservices and continuous deployment
Photographer: Avilov Alexandr
OWOX Business Intelligence
As business:
● More than 1 million transactions per week in our clients' projects
● 1 000+ projects in 50 countries rely on our solutions
● $ 200 000+ daily handled our clients' costs on advertising
As software:
● 10+ microservices in PHP/Java
● Web Components (Polymer 1.8)
● Several releases every day
Why am I here?
I want to share 9 improvements
for making frontend development easier
Who cares?
Frontend development
may be a constraint for your product
Let’s improve!
#1
Let frontend developer
don't deal with content management
#1
<div class="e-wrap">
<div class="e-grid-col1 e-grid-col-empty"></div>
<div class="e-grid-col8" role="main">
<h2 class="e-page-title">
Page not found
</h2>
<p class="error-message">
Incorrectly typed address or a page on the website no longer exists
</p>
<p class="error-link-wrap"><a href="/" class="error-link">
Home
</a></p>
</div>
<div class="clear"></div>
</div>
Do you really want
to change one selling text to another?..
#1
Use mnemonic names of texts
instead of hard-coded content
#1
Admin panel
for content
Write contentContent
manager
Frontend
developer Frontend
Write code
Matched by mnemonic name
#1
Polymer
<div class="e-wrap">
<div class="e-grid-col1 e-grid-col-empty"></div>
<div class="e-grid-col8" role="main">
<h2 class="e-page-title">
<html-text name="NotFoundPage.Title"></html-text>
</h2>
<p class="error-message">
<html-text name="NotFoundPage.Message"></html-text>
</p>
<p class="error-link-wrap"><a href="/" class="error-link">
<html-text name="HomePage.Title"></html-text>
</a></p>
</div>
</div>
Bonus: easy internalization
#1
#2
Let frontend developer
use HTML/JS templating
#2
.answers-i-link:before { CSS as Smarty (PHP) template
content: "";
position: absolute;
left: -30px;
width: 25px;
height: 25px;
background: url('/*{$settings.path}*//icons.png') 0 -414px no-repeat;
}
.answers-i-link:hover:before {
background: url('/*{$settings.path}*//icons.png') 0 -373px no-repeat;
}
.answers-i-link.active:before {
background: url('/*{$settings.path}*//icons.png') 0 -700px no-repeat;
}
.answers-i-link.active:hover:before {
background: url('/*{$settings.path}*//icons.png') 0 -659px no-repeat;
}
{foreach $tree as $section} HTML as Smarty (PHP) template
<h3 class="level-{$level}">{$section['title']}</h3>
{if $section['children']}
{include file='faq/subjects.tpl' tree=$section['children'] level=$level+1}
{/if}
{if isset($section['records'])}
{foreach $section['records'] as $record}
{if $is_contents}
<a href="#record-{$record.id}"
class="level-{$level}">{$record.title}</a><br />
{else}
<a name="record-{$record.id}"
class="level-{$level}">{$record.title}</a><br />
{$record['answer'] nofilter}
{/if}
{/foreach}
{/if}
{/foreach}
HTML as Smarty (PHP) template
<div class="b-expenses-head">
{foreach from=$manual_costs item="month" name="tabs"}
<div id="manual-costs-tab-{$smarty.foreach.tabs.index}" class="inline
b-expenses-head-month {if $month['is_active']}active{/if}">
<span class="b-expenses-month-title">{$month['month_title']}</span>
</div>
<script type="text/javascript">
ManualCosts.addGroup(new
ManualCostsTab_class({$smarty.foreach.tabs.index}, '{$month['hash']}'));
</script>
{/foreach}
</div>
JS as Smarty (PHP) template
initialize: function(data, connect_service_access) {
this.parent(data, connect_service_access);
Object.append(
this._templates,
{access_create: '/*{template_script_fetch file="access_create.jst" }*/'}
);
this._popup.addEvent('createAccess', this.onCreateAccess.bind(this));
},
onCreateAccess: function() {
this.send(
'/*{$menu.my.href}*/users/access/simple_services#createAccess',
{
service_name: this._data['service_name'],
token: token,
service_account: service_account
}
);
}
Do you really want to learn and debug
non JS/HTML templating?..
#2
Use HTML/JS templating,
use data as JSON
#2
Polymer
<template is="dom-if" if="[[!isEmpty(url)]]">
<page-item item-action>
<a is="pushstate-anchor" href="[[url]]" class="item-action">
<text-html name="Page.Item.Actions.Edit"></text-html>
</a>
</page-item>
</template>
Polymer
<dom-module id="page-external-link-styles">
<template>
<style include="shared-styles">
:host {
display: inline;
white-space: nowrap;
}
:host(:not(.no-link-style)) a {
color: var(--blue-color-main);
text-decoration: none;
}
</style>
</template>
</dom-module>
Bonus: static code analysis & tests
#2
#3
Let frontend developer
don't deal with backend routing
#3
Symfony (PHP) routing
# app/config/routing.yml
blog_list:
path: /blog/{page}
defaults: { _controller: AppBundle:Blog:list, page: 1 }
requirements:
page: 'd+'
blog_show:
# ...
Do you really want to learn and debug
backend routing?..
#3
Use frontend routing
via Single Page Application
#3
Polymer
<app-router id="appRouter" mode="pushstate" init="manual">
<app-route
id="dataPipeline"
path$="[[globals.pathPrefix]]/:context/pipeline/"
element="pipeline-page"
import="/components/pages/pipeline/pipeline-page.html"
></app-route>
<app-route
id="costDataPage"
path$="[[globals.pathPrefix]]/:context/pipeline/:property/:id/"
redirect="history/"
></app-route>
<app-route
id="costDataHistoryPage"
path$="[[globals.pathPrefix]]/:context/pipeline/:property/:id/history/"
element="cost-data-page"
import="/components/pages/cost-data/cost-data-page.html"
></app-route>
</app-router>
#4
Let frontend developer
don't deal with stagnated framework
#4
Switching framework is hard
● You can’t pause product development
● You can't release all changes at one time
#4
Two approaches
for framework switching
#4
1. Continuous deploy
then single release
#4
UI v1 (MooTools) → UI v2 (Polymer 0.5)
t
Release v1
Deploy v2
(part #1)
Change v1
prod
Deploy v2
(part #2)
Release v2
Deploy v2
(part #N)
...
Deploy != Release
#4
UI v1: https://domain.com/
UI v2: https://domain.com/v2/ ← SPA root
Continuous deploy then single release
+ Compatible with design switching
- Suitable only for small applications because of effort
duplication
#4
Off topic: why Polymer?
● Material Design out of the box
● Possibilities to UI reuse (not only JS)
● Google promotion of Web Components
● #UseThePlatform
#4
2. Page-by-page release
#4
The first law of holes
#4
Limitations:
1. Only one version of Polymer can be on page
2. We have Single Page Application
Solution: “break points” in Single Page Application
UI v2 — domain.com/v2/ UI v3 — domain.com/v3/
/foo/
/bar/
/baz/
/qux/
/foo/
/bar/
/baz/
/qux/
UI v2 (Polymer 0.5) → UI v3 (Polymer 1.8)
#4
UI v2
<app-router id="router" mode="pushstate" init="manual"
<app-route
id="pipeline"
path="/ui/:context/pipeline/"
import="{{$.globals.values.base_url}}pipeline-page/pipeline-page.html"
></app-route>
<app-route
id="cost_details"
path="/ui/:context/pipeline/:property/:id/"
redirect="history/"
></app-route>
<app-route
path="/ui/:context/pipeline/:property/:id/history/"
import="{{$.globals.values.base_url}}cost-page/cost-page.html"
new-page
></app-route>
</app-router>
<app-router id="appRouter" mode="pushstate" init="manual"> UI v3
<app-route
id="pipeline"
path$="[[globals.pathPrefix]]/:context/pipeline/"
element="pipeline-page"
import="/components/pages/pipeline/pipeline-page.html"
></app-route>
<app-route
id="costPage"
path$="[[globals.pathPrefix]]/:context/pipeline/:property/:id/"
redirect="history/"
></app-route>
<app-route
id="costHistoryPage"
path$="[[globals.pathPrefix]]/:context/pipeline/:property/:id/history/"
element="cost-data-page"
import="/components/pages/cost-data/cost-data-page.html"
old-page
></app-route>
</app-router>
+ Suitable for any size applications
- Only same design
- Time delays between UI transitions
Page-by-page release
#4
#5
Let frontend developer
deliver UI independently
#5
Delivery of UI with “monorepo” approach
1. Make changes
2. Wait till the whole monorepo can be deployed
3. Deploy
#5
Use standalone repository
and continuous delivery pipeline
#5
Delivery of UI with “standalone repo” approach
1. Make changes
2. Wait till the whole monorepo can be deployed
3. Deploy
#5
#6
Let frontend developer
use remote backend
#6
UI changes with “local backend” approach
1. Update backend source with dependencies
2. Update storage data
3. Update environment
4. Try to figure out via guide/FAQ why backend is not working
5. Call backend developer who knows how to update this
6. Spend half hour together to find stupid environment problem
7. Repeat 1-6 for each microservice
8. Make changes to frontend
#6
UI changes with “remote backend” approach
1. Update backend source with dependencies
2. Update storage data
3. Update environment
4. Try to figure out via guide/FAQ why backend is not working
5. Call backend developer who knows how to update this
6. Spend half hour together to find stupid environment problem
7. Repeat 1-6 for each microservice
8. Make changes to frontend
#6
request identity token
How to handle authorization?
identity token (JSON Web Token)
Backend
Attach header to each further request
Authorization: Bearer {identity token}
Frontend
#6
#6
#6
#6
#6
#6
#6
jwt.io
JSON Web Tokens libraries and debugger
#6
#7
Let frontend developer
don't deal with CORS
#7
#7
UI – https://domain.com/
https://foo.appspot.com/ https://bar.heroku.com/
Cross-origin resource sharing ?
#7
CORS (Cross-origin resource sharing)
● Request headers
○ Origin
○ Access-Control-Request-Method
○ Access-Control-Request-Headers
● Response headers
○ Access-Control-Allow-Origin
○ Access-Control-Allow-Credentials
○ Access-Control-Expose-Headers
○ Access-Control-Max-Age
○ Access-Control-Allow-Methods
○ Access-Control-Allow-Headers
#7
Use your web server’s ability
to serve UI and microservices
from single domain
#7
server {
server_name domain.com;
location /api/foo/ {
proxy_pass https://foo.appspot.com/;
}
location /api/bar/ {
proxy_pass https://bar.heroku.com/;
}
}
Nginx example
#7
#8
Let frontend developer
use production web server
without digging into its configuration
#8
Nginx
location /download/ {
valid_referers none blocked server_names *.example.com;
if ($invalid_referer) {
#rewrite ^/ http://www.example.com/
return 403;
}
# rewrite_log on;
# rewrite /download/*/mp3/*.any_ext to /download/*/mp3/*.mp3
rewrite ^/(download/.*)/mp3/(.*)..*$ /$1/mp3/$2.mp3 break;
root /spool/www;
#autoindex on;
access_log /var/log/nginx-download.access_log download;
}
Do you really want to learn and debug
production web server configuration?..
#8
Why “dev web server” approach may fail?
1. HTTP caching issues
2. Security issues
3. Missing redirects
#8
Use production-like Docker containers
with your local configuration
#8
.env
#8
DOMAIN=domain.com
FOO_ENDPOINT_URL=/api/foo
FOO_REAL_ENDPOINT_URL=https://foo.appspot.com/
BAR_ENDPOINT_URL=/api/bar
BAR_REAL_ENDPOINT_URL=https://bar.heroku.com/
...
.env
#8
.env
docker-compose
.override.yml
#8
version: '2'
services:
init:
environment:
FOO_REAL_ENDPOINT_URL: http://foo.custom
serve:
extra_hosts:
- "foo.custom:172.16.0.95"
docker-compose.override.yml
#8
.env
docker-compose
.override.yml
Configuration
$ docker-compose up init
Configured web server
$ docker-compose up -d serve
#8
#9
Let frontend developer
use production state
of concrete user
#9
How to fix a bug?
1. Prepare state
2. Reproduce
3. Fix
#9
● manual actions
● accesses to services
● historical entities
● data uniqueness
Let frontend developer
receive identity token of concrete user
but with read-only(!) permissions
#9
request identity token
“Looks like” feature
identity token of another user (read-only)
Backend
Attach header to each further request
Authorization: Bearer {identity token}
Frontend
#9
Frontend
developer
configure
#9
Recap
Let frontend developer:
1. don't deal with content management
2. use HTML/JS templating
3. don't deal with backend routing
4. don't deal with stagnated framework
5. deliver UI independently
6. use remote backend
7. don't deal with CORS
8. use production web server without digging into its configuration
9. use production state of concrete user
Questions?

More Related Content

What's hot (20)

PDF
Rock-solid Magento Deployments (and Development)
AOE
 
PPTX
An Intro into webpack
Squash Apps Pvt Ltd
 
PDF
Optimising Your Front End Workflow With Symfony, Twig, Bower and Gulp
Matthew Davis
 
PDF
Magento with Composer
AOE
 
PPTX
High Performance Snippets
Steve Souders
 
PPTX
What is HTML 5?
Susan Winters
 
PDF
Advanced front-end automation with npm scripts
k88hudson
 
PDF
Webpack DevTalk
Alessandro Bellini
 
PDF
Hitchhiker's guide to the front end development
정윤 김
 
PDF
webpack 101 slides
mattysmith
 
PDF
Webpack: from 0 to 2
Alessandro Bellini
 
PDF
When Web meet Native App
Yu-Wei Chuang
 
PDF
Puppeteer - A web scraping & UI Testing Tool
Miki Lombardi
 
PDF
Modern Web Application Development Workflow - EclipseCon France 2014
Stéphane Bégaudeau
 
PDF
Npm scripts
정윤 김
 
PDF
High Performance JavaScript - jQuery Conference SF Bay Area 2010
Nicholas Zakas
 
PPT
Js unit testing
Mihail Irintchev
 
PPTX
Your Script Just Killed My Site
Steve Souders
 
PDF
Clojure Web Development
Hong Jiang
 
PDF
WPE WebKit for Android
Igalia
 
Rock-solid Magento Deployments (and Development)
AOE
 
An Intro into webpack
Squash Apps Pvt Ltd
 
Optimising Your Front End Workflow With Symfony, Twig, Bower and Gulp
Matthew Davis
 
Magento with Composer
AOE
 
High Performance Snippets
Steve Souders
 
What is HTML 5?
Susan Winters
 
Advanced front-end automation with npm scripts
k88hudson
 
Webpack DevTalk
Alessandro Bellini
 
Hitchhiker's guide to the front end development
정윤 김
 
webpack 101 slides
mattysmith
 
Webpack: from 0 to 2
Alessandro Bellini
 
When Web meet Native App
Yu-Wei Chuang
 
Puppeteer - A web scraping & UI Testing Tool
Miki Lombardi
 
Modern Web Application Development Workflow - EclipseCon France 2014
Stéphane Bégaudeau
 
Npm scripts
정윤 김
 
High Performance JavaScript - jQuery Conference SF Bay Area 2010
Nicholas Zakas
 
Js unit testing
Mihail Irintchev
 
Your Script Just Killed My Site
Steve Souders
 
Clojure Web Development
Hong Jiang
 
WPE WebKit for Android
Igalia
 

Similar to Max Voloshin - "Organization of frontend development for products with microservices" (20)

PDF
Refactoring to a Single Page Application
Codemotion
 
PPTX
Refactoring to a SPA
Marcello Teodori
 
PPTX
How do we drive tech changes
Jaewoo Ahn
 
PPTX
Chandu.pptxhuuuuuuyyygfddssdfyuijhgghujjhhhhg
chch12775
 
PPTX
Micro Front Ends : Divided We Rule by Parth Ghiya - AhmedabadJS
KNOWARTH - Software Development Company
 
PPTX
Micro-Frontends JSVidCon
Amir Zuker
 
PPTX
Building assets on the fly with Node.js
Acquisio
 
PDF
Top 10 Latest Website Development Trends.pdf
CraftedQ CQ
 
PDF
Bd conf sencha touch workshop
James Pearce
 
PDF
Mastering Frontend Development A Comprehensive Guide To Learn Frontend Develo...
glummdemeod7
 
PDF
micro-frontends-with-vuejs
Oleksandr Tserkovnyi
 
PDF
Tech Thursdays: Building Products
Hayden Bleasel
 
DOCX
Understanding Front-End Development: Skills, Tools, and Trends
StudySection
 
PDF
JSFest 2019: Technology agnostic microservices at SPA frontend
Vlad Fedosov
 
PPTX
“Micro Frontends”- You Keep Using That Word, I Don’t Think It Means What You ...
Shem Magnezi
 
PDF
Architectures For Scaling Ajax
wolframkriesing
 
PDF
The Rise of BaaS A Utopia for Client-Side Developers
Marc Manthey
 
PDF
Web fundamentals
Nguyen Van Vuong
 
PDF
Frontend developer Roadmap .pdf
DurgeshSinghLodhi1
 
PDF
Single Page Web Apps
Jan Monschke
 
Refactoring to a Single Page Application
Codemotion
 
Refactoring to a SPA
Marcello Teodori
 
How do we drive tech changes
Jaewoo Ahn
 
Chandu.pptxhuuuuuuyyygfddssdfyuijhgghujjhhhhg
chch12775
 
Micro Front Ends : Divided We Rule by Parth Ghiya - AhmedabadJS
KNOWARTH - Software Development Company
 
Micro-Frontends JSVidCon
Amir Zuker
 
Building assets on the fly with Node.js
Acquisio
 
Top 10 Latest Website Development Trends.pdf
CraftedQ CQ
 
Bd conf sencha touch workshop
James Pearce
 
Mastering Frontend Development A Comprehensive Guide To Learn Frontend Develo...
glummdemeod7
 
micro-frontends-with-vuejs
Oleksandr Tserkovnyi
 
Tech Thursdays: Building Products
Hayden Bleasel
 
Understanding Front-End Development: Skills, Tools, and Trends
StudySection
 
JSFest 2019: Technology agnostic microservices at SPA frontend
Vlad Fedosov
 
“Micro Frontends”- You Keep Using That Word, I Don’t Think It Means What You ...
Shem Magnezi
 
Architectures For Scaling Ajax
wolframkriesing
 
The Rise of BaaS A Utopia for Client-Side Developers
Marc Manthey
 
Web fundamentals
Nguyen Van Vuong
 
Frontend developer Roadmap .pdf
DurgeshSinghLodhi1
 
Single Page Web Apps
Jan Monschke
 
Ad

More from IT Event (20)

PDF
Denis Radin - "Applying NASA coding guidelines to JavaScript or airspace is c...
IT Event
 
PDF
Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"
IT Event
 
PDF
Roman Romanovsky, Sergey Rak - "JavaScript в IoT "
IT Event
 
PDF
Konstantin Krivlenia - "Continuous integration for frontend"
IT Event
 
PPTX
Illya Klymov - "Vue.JS: What did I swap React for in 2017 and why?"
IT Event
 
PDF
Evgeny Gusev - "A circular firing squad: How technologies drag frontend down"
IT Event
 
PDF
Vladimir Grinenko - "Dependencies in component web done right"
IT Event
 
PDF
Dmitry Bartalevich - "How to train your WebVR"
IT Event
 
PDF
Aleksey Bogachuk - "Offline Second"
IT Event
 
PDF
James Allardice - "Building a better login with the credential management API"
IT Event
 
PDF
Fedor Skuratov "Dark Social: as messengers change the market of social media ...
IT Event
 
PPTX
Андрей Зайчиков "Архитектура распределенных кластеров NoSQL на AWS"
IT Event
 
PPTX
Алексей Рагозин "Java и linux борьба за микросекунды"
IT Event
 
PPTX
Volodymyr Lyubinets "Introduction to big data processing with Apache Spark"
IT Event
 
PDF
Наш ответ Uber’у
IT Event
 
PDF
Александр Крашенинников "Hadoop High Availability: опыт Badoo"
IT Event
 
PDF
Leonid Vasilyev "Building, deploying and running production code at Dropbox"
IT Event
 
PDF
Анатолий Пласковский "Миллионы карточных платежей за месяц, или как потерять ...
IT Event
 
PDF
Mete Atamel "Resilient microservices with kubernetes"
IT Event
 
PDF
Andrew Stain "User acquisition"
IT Event
 
Denis Radin - "Applying NASA coding guidelines to JavaScript or airspace is c...
IT Event
 
Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"
IT Event
 
Roman Romanovsky, Sergey Rak - "JavaScript в IoT "
IT Event
 
Konstantin Krivlenia - "Continuous integration for frontend"
IT Event
 
Illya Klymov - "Vue.JS: What did I swap React for in 2017 and why?"
IT Event
 
Evgeny Gusev - "A circular firing squad: How technologies drag frontend down"
IT Event
 
Vladimir Grinenko - "Dependencies in component web done right"
IT Event
 
Dmitry Bartalevich - "How to train your WebVR"
IT Event
 
Aleksey Bogachuk - "Offline Second"
IT Event
 
James Allardice - "Building a better login with the credential management API"
IT Event
 
Fedor Skuratov "Dark Social: as messengers change the market of social media ...
IT Event
 
Андрей Зайчиков "Архитектура распределенных кластеров NoSQL на AWS"
IT Event
 
Алексей Рагозин "Java и linux борьба за микросекунды"
IT Event
 
Volodymyr Lyubinets "Introduction to big data processing with Apache Spark"
IT Event
 
Наш ответ Uber’у
IT Event
 
Александр Крашенинников "Hadoop High Availability: опыт Badoo"
IT Event
 
Leonid Vasilyev "Building, deploying and running production code at Dropbox"
IT Event
 
Анатолий Пласковский "Миллионы карточных платежей за месяц, или как потерять ...
IT Event
 
Mete Atamel "Resilient microservices with kubernetes"
IT Event
 
Andrew Stain "User acquisition"
IT Event
 
Ad

Recently uploaded (20)

PDF
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
PDF
CIFDAQ Market Insights for July 7th 2025
CIFDAQ
 
PDF
Empower Inclusion Through Accessible Java Applications
Ana-Maria Mihalceanu
 
PDF
NewMind AI - Journal 100 Insights After The 100th Issue
NewMind AI
 
PDF
Transcript: New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
PDF
LLMs.txt: Easily Control How AI Crawls Your Site
Keploy
 
PDF
Achieving Consistent and Reliable AI Code Generation - Medusa AI
medusaaico
 
PPTX
Building Search Using OpenSearch: Limitations and Workarounds
Sease
 
PPTX
OpenID AuthZEN - Analyst Briefing July 2025
David Brossard
 
PDF
Jak MŚP w Europie Środkowo-Wschodniej odnajdują się w świecie AI
dominikamizerska1
 
PPTX
"Autonomy of LLM Agents: Current State and Future Prospects", Oles` Petriv
Fwdays
 
PDF
Newgen Beyond Frankenstein_Build vs Buy_Digital_version.pdf
darshakparmar
 
PDF
Log-Based Anomaly Detection: Enhancing System Reliability with Machine Learning
Mohammed BEKKOUCHE
 
PDF
July Patch Tuesday
Ivanti
 
PDF
The Builder’s Playbook - 2025 State of AI Report.pdf
jeroen339954
 
PDF
Exolore The Essential AI Tools in 2025.pdf
Srinivasan M
 
PDF
"AI Transformation: Directions and Challenges", Pavlo Shaternik
Fwdays
 
PDF
Smart Trailers 2025 Update with History and Overview
Paul Menig
 
PDF
Complete JavaScript Notes: From Basics to Advanced Concepts.pdf
haydendavispro
 
PDF
Building Real-Time Digital Twins with IBM Maximo & ArcGIS Indoors
Safe Software
 
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
CIFDAQ Market Insights for July 7th 2025
CIFDAQ
 
Empower Inclusion Through Accessible Java Applications
Ana-Maria Mihalceanu
 
NewMind AI - Journal 100 Insights After The 100th Issue
NewMind AI
 
Transcript: New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
LLMs.txt: Easily Control How AI Crawls Your Site
Keploy
 
Achieving Consistent and Reliable AI Code Generation - Medusa AI
medusaaico
 
Building Search Using OpenSearch: Limitations and Workarounds
Sease
 
OpenID AuthZEN - Analyst Briefing July 2025
David Brossard
 
Jak MŚP w Europie Środkowo-Wschodniej odnajdują się w świecie AI
dominikamizerska1
 
"Autonomy of LLM Agents: Current State and Future Prospects", Oles` Petriv
Fwdays
 
Newgen Beyond Frankenstein_Build vs Buy_Digital_version.pdf
darshakparmar
 
Log-Based Anomaly Detection: Enhancing System Reliability with Machine Learning
Mohammed BEKKOUCHE
 
July Patch Tuesday
Ivanti
 
The Builder’s Playbook - 2025 State of AI Report.pdf
jeroen339954
 
Exolore The Essential AI Tools in 2025.pdf
Srinivasan M
 
"AI Transformation: Directions and Challenges", Pavlo Shaternik
Fwdays
 
Smart Trailers 2025 Update with History and Overview
Paul Menig
 
Complete JavaScript Notes: From Basics to Advanced Concepts.pdf
haydendavispro
 
Building Real-Time Digital Twins with IBM Maximo & ArcGIS Indoors
Safe Software
 

Max Voloshin - "Organization of frontend development for products with microservices"

  • 2. Max Voloshin ● Location: Dnipro, Ukraine ● Position: Team Lead, OWOX ● In professional software development since 2010 ● Fan of microservices and continuous deployment Photographer: Avilov Alexandr
  • 3. OWOX Business Intelligence As business: ● More than 1 million transactions per week in our clients' projects ● 1 000+ projects in 50 countries rely on our solutions ● $ 200 000+ daily handled our clients' costs on advertising As software: ● 10+ microservices in PHP/Java ● Web Components (Polymer 1.8) ● Several releases every day
  • 4. Why am I here? I want to share 9 improvements for making frontend development easier
  • 5. Who cares? Frontend development may be a constraint for your product
  • 7. #1
  • 8. Let frontend developer don't deal with content management #1
  • 9. <div class="e-wrap"> <div class="e-grid-col1 e-grid-col-empty"></div> <div class="e-grid-col8" role="main"> <h2 class="e-page-title"> Page not found </h2> <p class="error-message"> Incorrectly typed address or a page on the website no longer exists </p> <p class="error-link-wrap"><a href="/" class="error-link"> Home </a></p> </div> <div class="clear"></div> </div>
  • 10. Do you really want to change one selling text to another?.. #1
  • 11. Use mnemonic names of texts instead of hard-coded content #1
  • 12. Admin panel for content Write contentContent manager Frontend developer Frontend Write code Matched by mnemonic name #1
  • 13. Polymer <div class="e-wrap"> <div class="e-grid-col1 e-grid-col-empty"></div> <div class="e-grid-col8" role="main"> <h2 class="e-page-title"> <html-text name="NotFoundPage.Title"></html-text> </h2> <p class="error-message"> <html-text name="NotFoundPage.Message"></html-text> </p> <p class="error-link-wrap"><a href="/" class="error-link"> <html-text name="HomePage.Title"></html-text> </a></p> </div> </div>
  • 15. #2
  • 16. Let frontend developer use HTML/JS templating #2
  • 17. .answers-i-link:before { CSS as Smarty (PHP) template content: ""; position: absolute; left: -30px; width: 25px; height: 25px; background: url('/*{$settings.path}*//icons.png') 0 -414px no-repeat; } .answers-i-link:hover:before { background: url('/*{$settings.path}*//icons.png') 0 -373px no-repeat; } .answers-i-link.active:before { background: url('/*{$settings.path}*//icons.png') 0 -700px no-repeat; } .answers-i-link.active:hover:before { background: url('/*{$settings.path}*//icons.png') 0 -659px no-repeat; }
  • 18. {foreach $tree as $section} HTML as Smarty (PHP) template <h3 class="level-{$level}">{$section['title']}</h3> {if $section['children']} {include file='faq/subjects.tpl' tree=$section['children'] level=$level+1} {/if} {if isset($section['records'])} {foreach $section['records'] as $record} {if $is_contents} <a href="#record-{$record.id}" class="level-{$level}">{$record.title}</a><br /> {else} <a name="record-{$record.id}" class="level-{$level}">{$record.title}</a><br /> {$record['answer'] nofilter} {/if} {/foreach} {/if} {/foreach}
  • 19. HTML as Smarty (PHP) template <div class="b-expenses-head"> {foreach from=$manual_costs item="month" name="tabs"} <div id="manual-costs-tab-{$smarty.foreach.tabs.index}" class="inline b-expenses-head-month {if $month['is_active']}active{/if}"> <span class="b-expenses-month-title">{$month['month_title']}</span> </div> <script type="text/javascript"> ManualCosts.addGroup(new ManualCostsTab_class({$smarty.foreach.tabs.index}, '{$month['hash']}')); </script> {/foreach} </div>
  • 20. JS as Smarty (PHP) template initialize: function(data, connect_service_access) { this.parent(data, connect_service_access); Object.append( this._templates, {access_create: '/*{template_script_fetch file="access_create.jst" }*/'} ); this._popup.addEvent('createAccess', this.onCreateAccess.bind(this)); }, onCreateAccess: function() { this.send( '/*{$menu.my.href}*/users/access/simple_services#createAccess', { service_name: this._data['service_name'], token: token, service_account: service_account } ); }
  • 21. Do you really want to learn and debug non JS/HTML templating?.. #2
  • 22. Use HTML/JS templating, use data as JSON #2
  • 23. Polymer <template is="dom-if" if="[[!isEmpty(url)]]"> <page-item item-action> <a is="pushstate-anchor" href="[[url]]" class="item-action"> <text-html name="Page.Item.Actions.Edit"></text-html> </a> </page-item> </template>
  • 24. Polymer <dom-module id="page-external-link-styles"> <template> <style include="shared-styles"> :host { display: inline; white-space: nowrap; } :host(:not(.no-link-style)) a { color: var(--blue-color-main); text-decoration: none; } </style> </template> </dom-module>
  • 25. Bonus: static code analysis & tests #2
  • 26. #3
  • 27. Let frontend developer don't deal with backend routing #3
  • 28. Symfony (PHP) routing # app/config/routing.yml blog_list: path: /blog/{page} defaults: { _controller: AppBundle:Blog:list, page: 1 } requirements: page: 'd+' blog_show: # ...
  • 29. Do you really want to learn and debug backend routing?.. #3
  • 30. Use frontend routing via Single Page Application #3
  • 31. Polymer <app-router id="appRouter" mode="pushstate" init="manual"> <app-route id="dataPipeline" path$="[[globals.pathPrefix]]/:context/pipeline/" element="pipeline-page" import="/components/pages/pipeline/pipeline-page.html" ></app-route> <app-route id="costDataPage" path$="[[globals.pathPrefix]]/:context/pipeline/:property/:id/" redirect="history/" ></app-route> <app-route id="costDataHistoryPage" path$="[[globals.pathPrefix]]/:context/pipeline/:property/:id/history/" element="cost-data-page" import="/components/pages/cost-data/cost-data-page.html" ></app-route> </app-router>
  • 32. #4
  • 33. Let frontend developer don't deal with stagnated framework #4
  • 34. Switching framework is hard ● You can’t pause product development ● You can't release all changes at one time #4
  • 36. 1. Continuous deploy then single release #4
  • 37. UI v1 (MooTools) → UI v2 (Polymer 0.5) t Release v1 Deploy v2 (part #1) Change v1 prod Deploy v2 (part #2) Release v2 Deploy v2 (part #N) ... Deploy != Release #4 UI v1: https://domain.com/ UI v2: https://domain.com/v2/ ← SPA root
  • 38. Continuous deploy then single release + Compatible with design switching - Suitable only for small applications because of effort duplication #4
  • 39. Off topic: why Polymer? ● Material Design out of the box ● Possibilities to UI reuse (not only JS) ● Google promotion of Web Components ● #UseThePlatform #4
  • 41. The first law of holes #4
  • 42. Limitations: 1. Only one version of Polymer can be on page 2. We have Single Page Application Solution: “break points” in Single Page Application UI v2 — domain.com/v2/ UI v3 — domain.com/v3/ /foo/ /bar/ /baz/ /qux/ /foo/ /bar/ /baz/ /qux/ UI v2 (Polymer 0.5) → UI v3 (Polymer 1.8) #4
  • 43. UI v2 <app-router id="router" mode="pushstate" init="manual" <app-route id="pipeline" path="/ui/:context/pipeline/" import="{{$.globals.values.base_url}}pipeline-page/pipeline-page.html" ></app-route> <app-route id="cost_details" path="/ui/:context/pipeline/:property/:id/" redirect="history/" ></app-route> <app-route path="/ui/:context/pipeline/:property/:id/history/" import="{{$.globals.values.base_url}}cost-page/cost-page.html" new-page ></app-route> </app-router>
  • 44. <app-router id="appRouter" mode="pushstate" init="manual"> UI v3 <app-route id="pipeline" path$="[[globals.pathPrefix]]/:context/pipeline/" element="pipeline-page" import="/components/pages/pipeline/pipeline-page.html" ></app-route> <app-route id="costPage" path$="[[globals.pathPrefix]]/:context/pipeline/:property/:id/" redirect="history/" ></app-route> <app-route id="costHistoryPage" path$="[[globals.pathPrefix]]/:context/pipeline/:property/:id/history/" element="cost-data-page" import="/components/pages/cost-data/cost-data-page.html" old-page ></app-route> </app-router>
  • 45. + Suitable for any size applications - Only same design - Time delays between UI transitions Page-by-page release #4
  • 46. #5
  • 47. Let frontend developer deliver UI independently #5
  • 48. Delivery of UI with “monorepo” approach 1. Make changes 2. Wait till the whole monorepo can be deployed 3. Deploy #5
  • 49. Use standalone repository and continuous delivery pipeline #5
  • 50. Delivery of UI with “standalone repo” approach 1. Make changes 2. Wait till the whole monorepo can be deployed 3. Deploy #5
  • 51. #6
  • 52. Let frontend developer use remote backend #6
  • 53. UI changes with “local backend” approach 1. Update backend source with dependencies 2. Update storage data 3. Update environment 4. Try to figure out via guide/FAQ why backend is not working 5. Call backend developer who knows how to update this 6. Spend half hour together to find stupid environment problem 7. Repeat 1-6 for each microservice 8. Make changes to frontend #6
  • 54. UI changes with “remote backend” approach 1. Update backend source with dependencies 2. Update storage data 3. Update environment 4. Try to figure out via guide/FAQ why backend is not working 5. Call backend developer who knows how to update this 6. Spend half hour together to find stupid environment problem 7. Repeat 1-6 for each microservice 8. Make changes to frontend #6
  • 55. request identity token How to handle authorization? identity token (JSON Web Token) Backend Attach header to each further request Authorization: Bearer {identity token} Frontend #6
  • 56. #6
  • 57. #6
  • 58. #6
  • 59. #6
  • 60. #6
  • 61. #6
  • 62. jwt.io JSON Web Tokens libraries and debugger #6
  • 63. #7
  • 64. Let frontend developer don't deal with CORS #7
  • 65. #7
  • 66. UI – https://domain.com/ https://foo.appspot.com/ https://bar.heroku.com/ Cross-origin resource sharing ? #7
  • 67. CORS (Cross-origin resource sharing) ● Request headers ○ Origin ○ Access-Control-Request-Method ○ Access-Control-Request-Headers ● Response headers ○ Access-Control-Allow-Origin ○ Access-Control-Allow-Credentials ○ Access-Control-Expose-Headers ○ Access-Control-Max-Age ○ Access-Control-Allow-Methods ○ Access-Control-Allow-Headers #7
  • 68. Use your web server’s ability to serve UI and microservices from single domain #7
  • 69. server { server_name domain.com; location /api/foo/ { proxy_pass https://foo.appspot.com/; } location /api/bar/ { proxy_pass https://bar.heroku.com/; } } Nginx example #7
  • 70. #8
  • 71. Let frontend developer use production web server without digging into its configuration #8
  • 72. Nginx location /download/ { valid_referers none blocked server_names *.example.com; if ($invalid_referer) { #rewrite ^/ http://www.example.com/ return 403; } # rewrite_log on; # rewrite /download/*/mp3/*.any_ext to /download/*/mp3/*.mp3 rewrite ^/(download/.*)/mp3/(.*)..*$ /$1/mp3/$2.mp3 break; root /spool/www; #autoindex on; access_log /var/log/nginx-download.access_log download; }
  • 73. Do you really want to learn and debug production web server configuration?.. #8
  • 74. Why “dev web server” approach may fail? 1. HTTP caching issues 2. Security issues 3. Missing redirects #8
  • 75. Use production-like Docker containers with your local configuration #8
  • 80. .env docker-compose .override.yml Configuration $ docker-compose up init Configured web server $ docker-compose up -d serve #8
  • 81. #9
  • 82. Let frontend developer use production state of concrete user #9
  • 83. How to fix a bug? 1. Prepare state 2. Reproduce 3. Fix #9 ● manual actions ● accesses to services ● historical entities ● data uniqueness
  • 84. Let frontend developer receive identity token of concrete user but with read-only(!) permissions #9
  • 85. request identity token “Looks like” feature identity token of another user (read-only) Backend Attach header to each further request Authorization: Bearer {identity token} Frontend #9 Frontend developer configure
  • 86. #9
  • 87. Recap Let frontend developer: 1. don't deal with content management 2. use HTML/JS templating 3. don't deal with backend routing 4. don't deal with stagnated framework 5. deliver UI independently 6. use remote backend 7. don't deal with CORS 8. use production web server without digging into its configuration 9. use production state of concrete user