Skip to content
All projects
08 · SHOPPING FROM ABROAD · CUSTOMER ACCOUNTLive site

CDEK.Shopping: 37% fewer regression bugs

Home page of the CDEK.Shopping website
  • E-commerce
  • Accounts

Stack:

  • JavaScript
  • jQuery
  • Vue 2
  • Vuex
  • SCSS
  • Webpack
  • Cypress
  • Jest
  • axios
  • portal-vue
  • Bootstrap
  • Popper
  • Owl Carousel
  • Slick
  • lightGallery
  • noUiSlider
  • jQuery Masked Input
  • Laravel
  • Blade
  • Sentry
  • Diginetica

used by me

Role
Middle Frontend Developer
Team
4 frontend, 8 backend, 2 QA and a designer
Timeline
2 years, 10.2021 – 10.2023
Status
The site is live, my work on the project is over

What I did

  • Customer accountThe entire frontend of the account in Vue 2 and Vuex: five sections in one app with no page reloads. I wrote every component of the account from the team designer's mockups.
  • Vue inside BladeConvinced the team to adopt Vue 2 and came up with a way to embed the SPA account into Laravel + Blade without a separate frontend project.
  • Legacy refactoringCovered the jQuery code with Cypress E2E tests and visual baselines, then split files of 6,000+ lines into modules with Jest unit tests.
  • Laravel and analyticsBlade templates, routes and account data in API responses. Set up Yandex Metrica and added events. Reviewed the tasks of the other frontend developers.

Context

Business

CDEK.Shopping is an agent for shopping abroad run by CDEK.Market LLC, a service of the CDEK group. Customers in Russia order products of brands from the US, Europe and Asia: they pay with a Russian card and see prices in rubles right away.

The service handles customs clearance and logistics, and orders are delivered by the group's own logistics company.

If the product is not in the catalog, the customer leaves a purchase request: sends a link to the product in a foreign store and specifies the size, color and quantity. A manager checks availability and calculates the final price in rubles with delivery and the service fee. After confirmation and payment, the service buys the product, and the order appears in the account on the “By purchase request” tab.

The situation

The business wanted to launch as soon as possible, so the new service was built on the CDEK Market codebase: jQuery and Blade, files of 6,000+ lines. The customer account there was built on server templates with no interactivity: every action reloaded the page, and the customer waited for the server at every step.

The accumulated tech debt slowed development down and was waiting to be solved, but the plan had no separate time for refactoring: it had to run alongside the launch, on the same deadlines.

Goal

Build an interactive customer account by the launch, where no action reloads the page. CDEK.Shopping users never saw the old account: they started right away in the new one.

Process, architecture and decisions

Customer account

The account is built as one Vue app: each section is a separate module with its own Vuex state and API requests. So the customer moves between sections without a page reload, and a new section can be added without touching the others.

  1. 01My detailsForms for contacts and customs clearance details, with the email and phone change moved into a separate flow
  2. 02Orders“All”, “Unpaid” and “By purchase request” tabs, a filter and pagination with no reload, the status of every item, a separate order page
  3. 03My pointsActive, accrued and frozen points from the loyalty program API, the points history
  4. 04Delivery addressChoosing the default address, switching between delivery to the door and to a pickup point
  5. 05ReviewsProducts waiting for a review and reviews already written, in one section
  6. 06NavigationCounters of unpaid orders and products without a review on the tabs, hash routing, a separate mobile navigation

Architecture

The storefront stayed server-rendered, while the account runs as a single-page app inside the same site. It did not need a separate frontend project.

  • STOREFRONTStorefront

    Server rendering with Blade, interactivity with jQuery.

  • ACCOUNTCustomer account

    Vue 2 and Vuex. The backend serves an #app container, the app mounts inside it, routing goes through the hash.

    • account
    • orderListing
    • orderDetailsPage
  • APIREST on Laravel

    The account gets its data through axios: profile, orders, points, delivery and reviews.

    • /api/v1/account
    • /api/v1/loyalty
    • /api/v1/customer
  • SERVICESExternal services

    Payments, the Mindbox marketing tracker, Diginetica search, Yandex Metrica.

Account components

We designed the component system together with the team designer, with BEM naming. The account has 16 base UI components: buttons, a checkbox, tabs, a select, a filter, a modal, a notification, a tooltip, pagination and others.

They make up about 40 section blocks, and the icons live in a sprite of about 30. I built the account responsive from the start, with a separate mobile navigation.

Integrations

  • Yandex Metrica · events
  • Online payments
  • Mindbox · marketing tracker
  • Diginetica · search with autocomplete

connected by me

Data security

The account works with customers' personal data, including passport details for customs clearance. They are stored encrypted in the database, and the profile API responses return them only masked. To see the data in full, the account makes a separate request to a protected endpoint. The server checks the user's session and permissions, decrypts the data and returns it only in that response. On the client it is not stored in Vuex or cached: once the block is closed, the mask is shown again.

Quality and releases

Before the refactoring I covered the store's critical scenarios with Cypress E2E tests and took visual baselines of the components. The modules that appeared after the refactoring are covered with Jest unit tests.

Every task goes through CI checks and a test stand, then on the planned release date it lands on a preprod identical to production. After acceptance the release rolls out to production with no downtime, and if something goes wrong we can quickly roll back to the previous stable version.

  1. develop test stand
  2. preprod acceptance
  3. master production, no downtime

We worked in two-week sprints with a demo at the end of each. Every task went through code review, and I reviewed the tasks of the other frontend developers.

Challenges

Legacy files of 6,000+ lines

Problem
The jQuery client code lived in files of 6,000+ lines. Writing unit tests for such spaghetti was impossible, and touching it without tests was risky.
Solution
First I covered the critical scenarios with Cypress E2E tests and took visual baselines, so the refactoring would break neither the logic nor the layout. Then, following the Refactoring.Guru principles, I moved repeated code into modules and kept splitting them until only pure functions covered with Jest unit tests were left.
Result
Regression bugs dropped by 37% according to QA reports. Features started shipping faster, and sprint overruns went down.

Vue 2 in a jQuery project

Problem
The whole project was written in jQuery and Blade. An interactive account needed a modern framework, and the team had to be convinced to adopt one.
Solution
Got approval to add Vue 2 to the account and embedded it without a separate project: the backend serves an #app container, the app mounts inside it, routing goes through the hash. The account was meant to show on a live example that writing in Vue is simpler and faster than maintaining the legacy code.
Result
The new service's account launched on Vue 2 and Vuex and became an argument for leaving the legacy code: the team decided to rewrite the project in Nuxt.

Metrics and results

  • −37%regression bugs after the refactoring
  • 5account sections in one app
  • 16base UI components of the account
  • ≤ 2.5 sLCP (Largest Contentful Paint)
  • ≤ 200 msINP (Interaction to Next Paint)
  • ≤ 0.1CLS (Cumulative Layout Shift)

CDEK.Shopping customers worked in the new account from day one, and the legacy refactoring cut regressions and sped up feature delivery.

Need a modern customer account in a legacy project?

I will embed an interactive account into your existing site without a separate frontend project, cover the legacy code with tests and split it into modules.