[{"data":1,"prerenderedAt":353},["ShallowReactive",2],{"project-case-qwixit":3},[4,13,19,73,89,106,142,281,316,340,346],{"component":5,"data":6},"ProjectCaseHeader",{"index":7,"subtitle":8,"name":9,"headline":10,"url":11,"tone":12},"01","ONLINE STORE · DELIVERY FROM THE US","Qwixit","300+ orders a month a few months after launch","https:\u002F\u002Fqwixit.com\u002F","primary",{"component":14,"data":15},"ProjectCaseCover",{"image":16,"imageMobile":17,"imageAlt":18},"qwixit\u002Fmedia\u002Fpreview.webp","qwixit\u002Fmedia\u002Fpreview-mobile.webp","Home page of the Qwixit website",{"component":20,"data":21},"ProjectCaseMeta",{"tags":22,"stack":27},[23,24,25,26],"E-commerce","Full-Stack","Payments","SEO",[28,31,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,63,65,67,69,71],{"label":29,"own":30},"TypeScript",true,{"label":32,"own":30},"Vue 3",{"label":34,"own":30},"SASS",{"label":36,"own":30},"Pinia",{"label":38,"own":30},"VueUse",{"label":40,"own":30},"Storybook",{"label":42,"own":30},"Laravel",{"label":44},"Go",{"label":46},"PostgreSQL",{"label":48},"Redis",{"label":50},"RabbitMQ",{"label":52},"S3 \u002F MinIO",{"label":54},"ClickHouse",{"label":56},"Elasticsearch",{"label":58},"Docker",{"label":60},"Traefik",{"label":62},"Kubernetes",{"label":64},"Argo CD",{"label":66},"GitLab CI",{"label":68},"ELK",{"label":70},"Grafana",{"label":72},"Prometheus",{"component":74,"data":75},"ProjectCaseFacts",{"facts":76},[77,80,83,86],{"key":78,"value":79},"role","Fullstack Developer",{"key":81,"value":82},"team","3 fullstack developers and a PM",{"key":84,"value":85},"duration","About 3 months to production",{"key":87,"value":88},"status","Completed, the site is live",{"component":90,"data":91},"ProjectCaseRole",{"id":78,"name":92,"items":93},"What I did",[94,97,100,103],{"title":95,"text":96},"The whole design","The storefront and the admin panel. About 50 reusable components are collected in Storybook and handed over to the client's team.",{"title":98,"text":99},"Frontend","Frontend architecture and the client side of the storefront and the admin panel in Vue 3 and Pinia, including the layer of requests to the backend.",{"title":101,"text":102},"Storefront API","Laravel API endpoints that feed the storefront.",{"title":104,"text":105},"Integrations and SEO","Payments, online chat, all the analytics and a Telegram bot that sends a notification about every order. The team had no SEO specialist, so I worked with Yandex Webmaster myself.",{"component":107,"data":108},"ProjectCaseChapter",{"id":109,"name":110,"blocks":111},"context","Context and starting point",[112,121,132],{"component":107,"data":113},{"id":114,"name":115,"blocks":116},"business","Business",[117],{"component":118,"data":119},"ProjectCaseText",{"text":120},"The client, the US company Pochtoy LLC, runs its own warehouse in Delaware. Its customers live in Russia and want genuine products from US stores and brands: New Balance, Nike, Adidas, Levi's, Tommy Hilfiger, Ralph Lauren, Calvin Klein, Michael Kors, Coach, iHerb.",{"component":107,"data":122},{"id":123,"name":124,"blocks":125},"situation","The situation",[126,129],{"component":118,"data":127},{"text":128},"US stores do not accept Russian cards and do not ship to Russia. The service covers the whole chain: the price is in rubles right away, payment by a Russian card or via SBP, a buyer purchases the item, delivery to a pickup point.",{"component":118,"data":130},{"text":131},"The client had no platform at all: neither a website nor an ordering process.",{"component":107,"data":133},{"id":134,"name":135,"blocks":136},"goal","The goal",[137],{"component":138,"data":139},"ProjectCaseGoal",{"value":140,"text":141},"300+","orders a month. Launch on a ready-made platform instead of building a store from scratch. The stack was set by the client and their team.",{"component":107,"data":143},{"id":144,"name":145,"blocks":146},"process","Process, architecture and decisions",[147,171,204,215,243,251],{"component":107,"data":148},{"id":149,"name":150,"blocks":151},"flow","The order flow",[152,155],{"component":118,"data":153},{"text":154},"The whole product is built around one chain, which the customer sees on the product page as the estimated delivery date.",{"component":156,"data":157},"ProjectCaseSteps",{"steps":158},[159,162,165,168],{"title":160,"text":161},"Payment","By a Russian card or via SBP, the price is in rubles right away",{"title":163,"text":164},"Purchase","A buyer purchases the item within an hour",{"title":166,"text":167},"Warehouse","Delaware, 4–7 days from the store",{"title":169,"text":170},"Delivery","Flights on Tuesdays and Thursdays, then CDEK: about 5 days to Moscow and Saint Petersburg, 10 days or more to the regions",{"component":107,"data":172},{"id":173,"name":174,"blocks":175},"architecture","Architecture",[176,179],{"component":118,"data":177},{"text":178},"The client wanted a ready-made platform, so the team took tlcommerce as the base and reworked it heavily.",{"component":180,"data":181},"ProjectCaseArchitecture",{"items":182},[183,187,196,200],{"label":184,"title":185,"text":186},"STOREFRONT","Storefront","A Vue 3 SPA with Pinia and SASS. Product pages are rendered on the server by Laravel.",{"label":188,"title":189,"text":190,"code":191},"API","Modular REST","Catalog, categories and site settings, menus and sliders, sections of the home page and the header, interface dictionaries.",[192,193,194,195],"ecommerce-core","theme","widgets","locale",{"label":197,"title":198,"text":199},"SERVICES","Go modules","Heavy queries and parallel computations live in Go modules, queues run on RabbitMQ.",{"label":201,"title":202,"text":203},"DATA","Storage","PostgreSQL as the main database, Elasticsearch for search and the catalog, Redis for cache and sessions, ClickHouse for analytics, S3 \u002F MinIO for media.",{"component":107,"data":205},{"id":206,"name":207,"blocks":208},"operator","A store that runs without developers",[209,212],{"component":118,"data":210},{"text":211},"The operator manages orders, products, promo codes and bonuses, changes banners and turns on holiday themes, for example for New Year. No release is needed for that: the home page and the header are assembled from widgets that come from the API.",{"component":118,"data":213},{"text":214},"Interface texts are loaded from a separate locale endpoint, so more languages can be added without a rewrite.",{"component":107,"data":216},{"id":217,"name":218,"blocks":219},"integrations","Integrations",[220,240],{"component":221,"data":222},"ProjectCaseIntegrations",{"items":223},[224,226,228,230,232,234,236,238],{"label":225,"own":30},"Payments by Russian cards and SBP",{"label":227,"own":30},"me-talk online chat",{"label":229,"own":30,"term":30},"Yandex Metrica",{"label":231,"own":30,"term":30},"Google Analytics",{"label":233,"own":30},"VK pixel",{"label":235,"own":30},"Telegram order bot",{"label":237},"CDEK · 25,000+ pickup points in 470 cities",{"label":239},"Maestra · newsletters and subscription pop-ups",{"component":118,"data":241},{"text":242},"The catalog is filled by parsing the websites of the stores the client is ready to deliver from.",{"component":107,"data":244},{"id":245,"name":246,"blocks":247},"security","Data security",[248],{"component":118,"data":249},{"text":250},"The service handles personal data of customers, including the data required for customs clearance. Sensitive fields are stored encrypted in the database, and the encryption key is kept apart from the code and the database. Access is split by roles: customer, operator, administrator.",{"component":107,"data":252},{"id":253,"name":254,"blocks":255},"quality","Quality and releases",[256,259,262,275,278],{"component":118,"data":257},{"text":258},"I wrote component tests in Vitest, visual regression tests against reference screenshots of blocks and E2E tests in Playwright for the critical scenarios.",{"component":118,"data":260},{"text":261},"Every task passes CI checks and a test stand, then on the planned release date it goes to a preprod stand identical to production. After acceptance the release rolls out to production without downtime, and in case of trouble it can be quickly rolled back to the previous stable version.",{"component":263,"data":264},"ProjectCasePipeline",{"stages":265},[266,269,272],{"name":267,"next":268},"develop","test stand",{"name":270,"next":271},"preprod","acceptance",{"name":273,"next":274},"master","production without downtime",{"component":118,"data":276},{"text":277},"We worked in two-week sprints. After each one we showed the client what was done, what was not and why, and agreed on the plan for the next two weeks.",{"component":118,"data":279},{"text":280},"At handover the client's team received the documentation: step-by-step setup, environment variables, the list of integrations, and Storybook with the components of the storefront and the admin panel.",{"component":107,"data":282},{"id":283,"name":284,"blocks":285},"challenges","Challenges",[286],{"component":287,"data":288},"ProjectCaseChallenges",{"items":289},[290,306],{"id":291,"name":292,"rows":293},"seo","SEO for an SPA",[294,297,300,303],{"key":295,"text":296},"problem","A customer comes from search looking for a specific product, while most of the storefront is a client-side Vue 3 app that search engines index poorly.",{"key":298,"text":299},"solution","I moved to the server only what search needs: product pages are rendered by Laravel, the rest stayed on the client.",{"key":301,"text":302},"result","Product pages are served from the server, and search engines see the H1, the price and the schema.org markup: Product, BreadcrumbList, Organization, FAQPage.",{"key":304,"text":305},"retro","I would move the client side to a separate Nuxt app: the catalog is only partly rendered on the server, and the home page and the information pages stayed client-side.",{"id":307,"name":308,"rows":309},"load","A million products and the load",[310,312,314],{"key":295,"text":311},"Complex queries and heavy computations over the catalog loaded the main database.",{"key":298,"text":313},"As a team we rewrote some modules in Go for complex queries and parallel computations, and put Elasticsearch in front of the database to serve products fast.",{"key":301,"text":315},"Search and the catalog do not overload the database, and load peaks passed without noticeable problems.",{"component":107,"data":317},{"id":318,"name":319,"blocks":320},"results","Metrics and results",[321,337],{"component":322,"data":323},"ProjectCaseResults",{"stats":324},[325,328,331,334],{"value":326,"label":327},"300–500","orders a month",{"value":329,"label":330},"~1M","products in the catalog at handover",{"value":332,"label":333},"40–70k","visitors a month",{"value":335,"label":336},"~3 mo","of development before launch",{"component":118,"data":338},{"text":339},"The client's goal of 300+ orders a month was reached a few months after launch.",{"component":341,"data":342},"ProjectCaseCta",{"id":343,"title":344,"text":345,"url":11},"cta","Need an online store with a large catalog or sales to another country?","I will design the storefront and the admin panel, build them, connect payments and analytics and take the store to launch.",{"component":347,"data":348},"ProjectCaseNext",{"slug":349,"name":350,"index":351,"tone":352},"brendmart","Brendmart","02","secondary",1792947428851]