{"id":16809979,"url":"https://github.com/wowinter13/real_life_interview","last_synced_at":"2025-11-02T00:30:35.952Z","repository":{"id":150344176,"uuid":"143614122","full_name":"wowinter13/real_life_interview","owner":"wowinter13","description":"Some questions from real life interviews. Ruby/Rails, Golang and other backend nuances.","archived":false,"fork":false,"pushed_at":"2023-10-04T15:32:50.000Z","size":656,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-12-26T18:46:57.234Z","etag":null,"topics":["interview","ruby","sql-queries"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wowinter13.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2018-08-05T13:15:03.000Z","updated_at":"2021-10-29T18:39:40.000Z","dependencies_parsed_at":null,"dependency_job_id":"e263c4ec-5801-4f4e-b7f9-01447efaee0d","html_url":"https://github.com/wowinter13/real_life_interview","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wowinter13%2Freal_life_interview","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wowinter13%2Freal_life_interview/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wowinter13%2Freal_life_interview/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wowinter13%2Freal_life_interview/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wowinter13","download_url":"https://codeload.github.com/wowinter13/real_life_interview/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239349873,"owners_count":19624204,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["interview","ruby","sql-queries"],"created_at":"2024-10-13T10:14:19.604Z","updated_at":"2025-02-17T19:14:52.528Z","avatar_url":"https://github.com/wowinter13.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Technical Interview Cheat Sheet\n\nA collection of questions from different technical interviews.\n\n***\n\n## Contributing\nThis is an open source, community project, and I am grateful for all the help I can get. If you find a mistake make a PR and please have a source so I can confirm the correction. If you have any suggestions feel free to open an issue.\n\n***\n\n# Table of Content\n- [Computer Science](cs.md)\n- [Databases](db.md)\n  - [DB Assessments](sql_assessments.md)\n- [Architecture/Systems Design](arch.md)\n- [Ruby \u0026 Rails](ruby.md)\n  - [Ruby Assessments](ruby_assessments.md)\n- [Golang](golang.md)\n  - [Go Assessments](go_assessments.md)\n***\n\n# \u003ca id=\"common\"\u003e\u003c/a\u003e Common\n### \u003ca id=\"algorithms\"\u003e\u003c/a\u003e Algorithms\n\n### \u003ca id=\"concurrency-and-parallelism\"\u003e\u003c/a\u003e Concurrency and Parallelism\n\n#### 1. Threads vs Processes\n\n\u003cimg width=\"560\" height=\"544\" src=\"https://raw.githubusercontent.com/wowinter13/real_life_interview/master/thread-vs-process.png\"\u003e\n\n\n\n\n\n***\n\n\n### \u003ca id=\"system-design\"\u003e\u003c/a\u003e System Design\n\n**Распределенные системы**\n\n1. Replication\nхранение копий одних и тех же данных на нескольких машинах, i.e. дубликация\n\nРешает проблему географии (когда пользователи разбросаны по миру), проблему отказа одной из систем (высокая доступность) и простое горизонтальное масштабирование (обработка большего количества запросов).\n\nСложность репликации – изменение данных на всех узлах.\n\nПо типам разделяют репликацию с одним ведущим узлом (single leader), с несколькими ведущими узлами (multi-leader) и без ведущего узла (leaderless).\n\nТе узлы, которые хранят копии называются реплики.\n\nБазовый вариант реализации master/slave (leader-based replication). Лидер узла отвечает за запись данных в свое локальное хранилище, а затем используя журнал репликации (replication log) рассылает все обновления в ведомые узлы (followers). Данная реализация доступна во всех современных БД и некоторых брокерах сообщений.\n\nПо типам выполнения разделяют синхронную, асинхронную и полусинхронную репликацию.\n\nСинхронная репликация обеспечивает гарантии актуальных данных между узлами, но в некоторых случаях операция записи может занимать долгое время. Проблема может возникнуть, если follower реплика не отвечает и произойдет rollback на ведущей реплике.\n\nЧаще используют semi-синхронную репликацию, в данном случае синхронное обновление происходит только на одном ведущем и ведомом узле.\n\nВ случае отказа ведомого узла, проблема не возникает – ведущий узел хранит лог изменений и воспроизведет его при рестарте. В случае отказа ведущего узла, можно временно можно назначить ведомый узел в качестве ведущего.\n\n\nРепликацию с несколькими ведущими узлами используют в развертывании систем, расположенных в нескольких ЦОДах.\nВ postgres это BDR (bi-directional replication). Чаще всего конфликты между асинхронными записями решаются алгоритмом LWW (last write wins).\n\nБез ведущего узла – экспериментальный вариант, используется в Dynamo-подобных системах. Запросы падают сразу на все узлы.\n\n2. Партицирование (шардирование, секционирование)\n\nСекционирование это разбиение базы на отдельные кусочки. Снова же основная цель партицирования – масштабируемость системы, применяется когда набор данных настолько велик, что репликации становится недостаточно. Основная идея заключается в том, что при партицировании не используется разделение ресурсов.\n\nЦель секционирования — равномерно распределить по узлам данные и загрузку по запросам. В случае если нагрузка распределена неравномерно, секционирование называют ассиметричным (skewed), а секцию с максимальной нагрузкой называют hot spot. Партицирование может быть как и автоматическим (по хеш-ключу), так и ручным. Ручное устанавливается по каким-либо диапазонным значениям. Для распределения запросов используется балансировщик нагрузок.\n\nИндексы также требуется секционировать – локальные в рамках секции и глобальные между секциями.\n\n**В случае postgresql часто используется связка partman/pg_cron для автоматического партицирования данных в какой-то период. (PARTITION BY)**\n\n3. Как достигается консенсус в распределенных системах?\n\nИспользуется алгоритм двухфазной фиксации (**2PC, two phase commit**). Этот алгоритм гарантирует, что все узлы либо зафиксировали транзакцию, либо прервали ее. Для этого используется диспетчер транзакций. \n\n3. Stateful and stateless applications. \n\n\n\n# \u003ca id=\"databases\"\u003e\u003c/a\u003e Databases\n\n### \u003ca id=\"common-databases\"\u003e\u003c/a\u003e Common\n\n\n**Types of indexes**\n\n1. Hash-indexes (key-value)\n\nUsed for columns containing unique data. \n\nA hash index stores keys by dividing them into smaller buckets, where each bucket is given an integer ID-number to retrieve it quickly when searching for a key’s location in the hash table. The buckets are stored sequentially on a disk. Fast read, cheap storage. But it is impossible to store duplicate keys within a bucket.\n\n2. LSM-tree (Log-Structured Merge-Tree)\n\n3. B-trees (balanced tree)\n\nThe most popular index type.\n\nBalanced tree is balanced because compared to hash or log-strucured indexes, B-trees always split keys by dividing them into chunks (page) with a fixed size (about 4Kb). Each page has a pointer (link) on the disk, and can be a reference to another one page. The links count for child elements called is called branching factor (for the sake of simplicity, depth).\n\n**ACID**\n\nACID – atomacity, consistency, isolation, durability (атомарность, консистентность, изоляция, сохраняемость).\n\n**А**томарность – прерываемость. Возможность прервать транзакцию при ошибке. Принцип \"Все или ничего\".\n\n**C**onsistency – гарантия целостности данных. Можно сказать что если транзакция начинается при допустимом состоянии базы данных, то можно быть уверенным, что и после состояние БД будет соответствовать изначальным инвариантам (изначальным утверждениям).\n\n**I**solation – гарантирует, что конкурентные операции изолированы друг от друга. \n\n**D**urability – гарантирует, что данные не будут потеряны после успешно зафиксированных операций даже в случае сбоя самой БД.\n\n**CAP теорема**\n\nCAP – consistency, availability, partition tolerance (2/3).\n\n**C**onsistency – согласованность.\n\n**A**vailability – доступность.\n\n**P**artition tolerance – устойчивость к нарушениям связности.\n\nТеорема вызывает большего вопросов, чем ответов, тк даже самые современные и устойчивые системы не могут гарантировать соответствие условиям теоремы.\n\nГоворя про согласованность, она нарушается уже при использовании многоядерных процессоров, если поток на одном ядре пишет в ячейку, а поток на другом читает – без использования барьеров памяти ошибка может возникнуть уже на таком уровне.\n\nДоступность может быть \"гарантирована\" с помощью использования систем репликации и партиционирования, но при этом увеличивая вероятность нарушений связности.\n\n\n\n### \u003ca id=\"postgresql\"\u003e\u003c/a\u003e Postgresql\n\n#### 1. Do you know what is PGQ? Other queues in Postgres?\n\n\n#### 2. Postgres. Indexes.\n\nПо типам индексов, их там в PG штук 5, но я лично сталкивался в миграциях только с btree и ginом.\nbtree это дефолтный индекс – balanced дерево. Получается работает как придавленное нормальное распределение, то есть, у нас очень ветвистое дерево, весь кайф в балансе, то есть глубина на всех участках одинаковая. Собственно по понятным причинам подходит для сортируемых данных.\ngin - это базовый обратный индекc, там концепция обратная – мы храним не значения по индексам, а индексы по значениям. \nЕще там есть Brin, как Сергей Брин, но индекс, Gist, хеш-индексы, но детали не изучал ибо не использовал осознанно.\n\n\n### \u003ca id=\"nosql-databases\"\u003e\u003c/a\u003e NoSQL\n\n#### 1. What are types of NoSQL databases?\n\ngraph stores, key-value stores, document stores, column stores.\n\n\n***\n\n# \u003ca id=\"ror\"\u003e\u003c/a\u003e Ruby on Rails\n\n### \u003ca id=\"ruby\"\u003e\u003c/a\u003e Ruby\n\n### DB Questions\n\n**What is N+1 query?**\n\nThe N+1 query is a query, when we do a one more query for a child-record each time iterating throught parent-records.\n\nThe main problem is a reduced performance.\n\n\n\n**Basic SQL Clauses**\n- HAVING\n- GROUP\n\n**Join types:**\n- INNER\n- OUTER\n- LEFT RIGHT\n- FULL\n\n**Why do we need a transaction when getting data from the database?**\n\nТранзакции обеспечивают гарантии функциональной безопасности. Упрощают обработку ошибок конкурентного доступа. \nВ случае состояния гонки это позволить сделать `LOCK` базы для получения ожидаемого результата.\n\nЕсли более точно, то существует 4 уровня изоляции данных. По дефолту в PG используется `read commited`, который читает записи закомиченные до исполнения запроса. Транзакция позволяет исполнять запрос в рамках одного соединения и получать корректные данные на выходе.\n\n**Что такое индекс, когда добавляют в базу и зачем они нужны**\n\nИндекс — дополнительная структура, производная от основных данных.\n\nИндекс это обмен памяти на скорость, и базе данных вместо того чтобы использовать полнотекстный поиск достаточно пройтись по индексу, это будет значительно быстрее.\n\nИндексы влияют на скорость апдейта и инсерта, потому что при каждом изменение таблицы нужно перестраивать индекс.\n\n**Как устроен btree и как функция защищает от коллизий**\n\n\n**What is pgBouncer? When should we use it?**\n\nНадстройка управляющая пулом соединений к Postgresql. Создает сеансы(сессии) для пользователей бд в рамках пула открытых соединений, что позволяет не открывать новое соединение для каждого из новых пользователей.\n\nИспользовать имеет смысл, когда база упирается в высокое количество клиентских соединений из-за чего транзакция занимает миллисекунды, но создание соединения секунды.\n\n**Postgres replication (like: master-slave)**\n\n\n**Базовые уровни изоляции транзакций**\n\n- `Read Commited`\n    Default reading mode. Гарантирует, что любые закомиченные данные будут прочитаны. Вызывает lock на запись в таблице -\u003e ставит в очередь другие транзакции. Запрещает Dirty Read. Обычно реализуется через блокировку  транзакцией записи до модификации значения.\n- `Read Uncommitted`\n    Разрешает Dirty Read. Самый низкий уровень изоляции. Позволяет читать еще не закомиченные данные. Транзакции не изолированы друг от друга.\n- `Repeatable Read`\n    Транзакция вызывает lock на все записи на чтение и обновление в таблице, тк другие транзакции не имеют доступ к данным -\u003e исключается аномалия `Non Repeatable read` (когда при двойном прочтении транзакция получает разный результат)\n- `Serializable`\n    Доступ, используемый в программах для создания дампов БД. Максимальный уровень изоляции транзакций. Граф сериализации выполняется последовательно -\u003e становятся недопустимы любые аномалии данных. Последовательное исполнение транзакций. \n\n***\n\n### Theoretical questions\n\n**Кому принадлежат методы объекта класса**\n\nОни принадлежат все равно классу, потому что хранятся в его структуре.\n\n**3 принципа ООП:**\n\n\n**How incapsulation works in Ruby?**\n\n**5 principles SOLID:**\n\n*SOLID is an acronym for 5 basic OOP principles based on making the code more understandable, flexible and maintainable.*\n\n- **S(ingle resposibility)**\n  One class should have only one responsibility.\n- **O(pen-closed)**\n  Class should be open for extension, but closed for modification.\n- **L(iskov substitution)**\n  All subtype class instances should not break the behavior of the original ancestor class, so they can be substituted.\n- **I(nterface segregation)**\n  Many interfaces of special usage (client-specific) are better than one universal and abstract interface.\n- **D(ependency inversion)**\n  Abstractions should not depend on details. Details should depend on abstractions.\n\n**Cвязность и что это означает**\n\n*Классы должны как можно меньше знать друг о друге.*\n\n\n***\n\n### Practical questions\n\n**What is the difference between Proc and Lambda?**\n\nProc - это объект. Прок не проверяет количество аргументов, а лямбда проверяет. Ну и return разный, лямбда возвращает результат как метод, а proc результат выполнения.\n\n\n\n**What is Mixin?**\n\n**Multiple inheritance in Ruby**\n\n**The most known/used metaprogramming example in Ruby**\n\n*attr_accessor, attr_reader, attr_writer*\n\n**What are unit tests? And when do we use them?**\n\n**Where should we store the business logic?(fat models/fat controllers)**\n\n*Better to use some basic patterns like: interactors, services or operations, and to store it here.*\n\n**Basic Ruby/Rails patterns:**\n- Interactor\n- FormObject\n- ServiceObject\n\n**What is MVC**\n\n## Server-side questions\n\n**Чем отличается puma от unicorn?**\n\n**Fork/Thread. Что это и в чем разница?**\n\n`Поток` создается в рамках одного процесса (параллелизм). `Форк` это создание новой отдельной копии процесса.\n\nПотоки расходуют меньше памяти тк не создается новый инстанс задачи.\n\nНо форки процесса наследуют ресурсы родителя, в том числе и коннекшены к базе данных.\nПоэтому внутри каждого форка нужно заново переоткрывать соединение к БД.\n\nИспользуя `MRI` лучше форкать, `jruby` - создавать треды, тк MRI запускает только 1 поток в моменте.\n\n**Зачем нужен NGINX?**\n\nNginx - веб-сервер. Puma или Unicorn - application-сервер.\n\nВ NGINX'е есть базовая защита от DDOS'а.\nОн лучше обрабатывает multipart-загрузку файлов.\n\nСлужит для обработки статики и ассетов.\n\nРедирект.\n\n**Static File Serving**: Nginx excels at serving static files directly. This allows you to offload serving static files (CSS, JavaScript, images, etc.) from your Ruby application server (like Puma) to Nginx, thus freeing up Puma to focus on serving dynamic content. This can lead to a more responsive application and better use of server resources.\n\n**Load Balancing:** Nginx can be used as a reverse proxy and a load balancer.\n\n**Caching**\n\n**Rate Limiting and Security**: Nginx can perform rate limiting.\n\n\n\n**Как работает loadbalancer**\n\n\n### \u003ca id=\"metaprogramming\"\u003e\u003c/a\u003e Metaprogramming\n\n\n### \u003ca id=\"rb-concurrency-and-parallelism\"\u003e\u003c/a\u003e Concurrency and Parallelism\n\n#### GIL. Threads.\n\nGIL (global interpretation lock) is a thread synchronization mechanism used in Ruby that helps to avoid data conflicts when more than 1 thread is accessing the same area of memory.\n\nAnd threads are used to parellelize computations. Threads are placed in a special context and can share the same memory.\n\n### \u003ca id=\"ancient-magic\"\u003e\u003c/a\u003e Ancient Magic (Ruby under a Microscope)\n\n\n\n**Ruby Garbage Collector**\nGC collects all unused objects and frees memory of them. Basically, GC will delete object, if there are no more calls to it in the program.\n\n\n## Patterns\n\n**Form object**\n\nНе паттерн, а подход к обработке данных (по сути это замена permitted_params).\n\nПолучаем возможность препроцессить данные, валидировать их (dry-struct, dry-types, dry-validations).\n\n**Decorator**\n\nДекорирует только один объект (добавляет некоторую функциональность) (draper)\n\n**Presenter**\n\nТоже декоратор, но уже максимально приближенный к вьюхам.\n\n**Policies**\n\nНе назвал бы паттерном, просто подход к делегированию информации и доступов (pundit).\n\n**Serializers**\n\nСпособ нормализовать данные и стандартизировать API. (json serializer)\n\n**Interactor, Service object**\n\nИнкапсулирует часть бизнес логики (Command паттерн) (dry-transaction (чейнит шагами) -\u003e Interactor::Organizer + Interactor).\n\n**The difference between Presenter \u0026 Decorator**\n\nНа каждом проекте по разному делают в итоге. Но однозначно говоря - декоратор добавляет функциональность объекта. А презентер это сервис который подготавливает информацию к отображению (например обработка коллекции из сущностей с информацией из связей). Декоратор - это общее решение: вроде универсального форматирования. А презентер используется для какого-то узкого назначения.\n\n\n### \u003ca id=\"concurrency-and-parallelism\"\u003e\u003c/a\u003e Concurrency and Parallelism\n\n#### 1. Mutex \u0026 RWMutex. What is the difference between them?\n\n\n***\n# \u003ca id=\"javascript\"\u003e\u003c/a\u003e Javascript\n\n#### What is the difference between `let`, `const` and `var` in JS.\n\n#### How does OOP implemented in vanilla JS?\n\n#### In cookie-based session management, who sets the session token?\n\n\n\n\n\n// to sort\n#### Why gRPC is better than our lovely REST? Why gRPC is a perfect choice for microservices? Do you know any useful features?\n\ngRPC is a perfect choice for microservices simply because of being lightweight and fast. Talking about the advantages:\n1. Metadata. Instead of using HTTP requests. Metadata is much simplier and perfectly fits for internal communication.\n2. Streaming (thanks to HTTP 2.0). gRPC provides all streaming types (client, server, bidirectional).\n3. Interceptors. The way gRPC allows you to modify and change requests/responses right out of the box. (read: middlewares)\n4. Load Balancing.\n5. Call Cancellation. You can simply kill a gRPC call if you don't need a response anymore.\n\n#### RabbitMQ Clustering?\n\n#### Quorum queues\n\n\n#### PG: express index. Which index should be used for hashes and nested hashes.\n\n#### 3. Garbage Collector в руби.\nGC собирает неиспользуемые объекты и освобождает память от них. GC удаляет объект, если в программе больше нет его вызовов.\n\n#### 4. include, extend, prepend\ninclude - подгружает методы инстанса\nextend - подгружает методы класса\nprepend - подгружает методы инстанса ниже всего по ast дереву, то есть они самые первые\n\n#### 5. Сложность поиска по ключу в хеше:\nO(1), O константы\n\n#### 6. коллизии в хешах\nситуация когда несколько ключей содержат одно значение, и получается что так как хеш это массив содержащий связанный списки, то получается что у нас образуется цепочка длинной больше чем 1. Я детали не изучал, знаю что руби справляется с этим через чейнинг, поэтому сложность если и увеличивается то в рамках адекватного.\n\n\n# \u003ca id=\"network\"\u003e\u003c/a\u003e Network \u0026 DevOps\n\n#### 1. TCP vs UDP\n\nTCP считает пакет потерянным после отсутствия ответа по истечении некоторого времени ожидания и автоматически повторяет пересылку потерянных пакетов. Надежный, нацелен на доставку.\n\nUDP имеет смысл выбирать в случаях, когда запоздавшие данные теряют всякую ценность. Потери пакетов уместны. Стриминг, видео звонки и тд. \n\n\n// TO sort\n\nQ1. Массив и хеш - отличия и сложность.\nA1. По массиву, у нас для любых поисков и удалений O(1) константы, для поиска по значению O(n). Все просто: O(1), тк в этом мире все использует ссылки, назовем их поинтеры, то массив устроен так: выделяется место в памяти под массив, но каждый элемент выделен участок памяти конкретного размера, условно когда мы делаем метод index, то мы просто берем точку в памяти и умножаем индекс на количество выделенной памяти.\nПри операциях со значениями O(n), потому что надо пройтись по всему размеру. Адресная арифметика.\nС хешом, та же кошка вид сзади. Ну то есть на практике там все-равно кажется две операции идет, но условно сложность как и в массивах. При работе с ключами O(1), со значениями O(n).Тут все просто, хеш на самом деле массив, который хранит в себе ссылки. Цель хеширования перегонка данных к фиксированному размеру ячейки.\n\nQ2. Сложность разворота массива?\nA2.\nQ3. Как работает выделение памяти?\nA3. На примере массива. Руби аллоцирует память для массива, он не учитывает конечную длину. А когда мы добавляем данные, то происходит перенос всего объекта памяти. Руби выделяет ячейку большего размера и переносит туда весь массив целиком.\nКак решить проблему выделения памяти? Ну на самом деле на примере из Си и Гоу скажу, достаточно задать изначальную длину массива я так думаю.\nQ4: Руби не типизирован, почему все работает эффективно ведь в массив можно кинуть какие угодно данные какого уугодно размера?\nA4: Ну механизм ссылок-поинтеров, как раньше уже упомянал, мы храним только ссылки на объект.\n\n\nПо типам индексов, их там в PG штук 5, но я лично сталкивался в миграциях только с btree и ginом.\nbtree это дефолтный индекс – balanced дерево. Получается работает как придавленное нормальное распределение, то есть, у нас очень ветвистое дерево, весь кайф в балансе, то есть глубина на всех участках одинаковая. Собственно по понятным причинам подходит для сортируемых данных.\ngin - это базовый обратный индекc, там концепция обратная – мы храним не значения по индексам, а индексы по значениям. В детали не углублялся это уже для любителей NLP и всяких векторных корпусов из слов и тд. Просто этот тип лучше подходит для текста. Еще там есть Brin, как Сергей Брин, но индекс, Gist, хеш-индексы, но детали не изучал ибо не использовал осознанно.\n\nВиды серверов:\n1. Puma, Unicorn, Nginx, Apache\nPuma, Unicorn – это application сервера.\nNginx, Apache – это веб-сервер.\nУ них разное назначение, веб-сервер это вроде гейткипера, он обрабатывает загрузку файлов, обрабатывает статику, картинки, стили, ассеты. Еще выступает прослойкой от DDOS'а, условно чтоб не происходили нагрузки напрямую на app server. Еще nginx умеет в редирект. В общем, web-сервер стоит между клиентом и app сервером.\n\nРазница пумы и юникорна – пума многопоточный, а юникорн держит в процессе один поток. Гитлаб недавно на пуму переезжал.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwowinter13%2Freal_life_interview","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwowinter13%2Freal_life_interview","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwowinter13%2Freal_life_interview/lists"}