Задать вопрос
Мы специализируемся на разработке сложных диджитал-продуктов и из тех компаний, где каждый одержим тем, что делает. Где сотрудникам доверяют, за ошибки не ругают, а работать можно сколько и где хочешь. Если вы из тех, кто горит своим делом и подыскиваешь для себя райское место, напиши нам на hr@atnr.pro и вполне возможно, что наши дороги пересекутся.

Вклад в сообщество для нас ценность: в свое время именно помощь сообщества позволила каждому из нас стать тем, кем мы стали. При этом, написать ответ от лица компании может абсолютно любой наш сотрудник от мидла фронта до руководителя команды.

Если же вы в поиске кому бы отдать разработку вашего софта и вам нравятся наши ценности и ответы, пишите нам по любому удобному вам каналу. Разговоры с нами не только бесплатны, но еще и полезны для вас и вашего бизнеса )
Контакты
Местоположение
Россия, Москва и Московская обл., Москва

Достижения

Все достижения (3)

Наибольший вклад в теги

Все теги (51)

Лучшие ответы пользователя

Все ответы (71)
  • Где искать программистов для проекта?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    Давайте сначала про дороговизну )

    Как правило, найти сильного разработчика, работающего на себя, за которым не нужен отдельный проджект-менеджер, аналитик в помощь и тестировщик, это редкость и если рассматривать индивидуальных разработчиков, то сейчас сильные фронтендеры, которые обладают достаточным объемом как технических, так и коммуникационных навыков, зарабатывают в районе 170-250к до вычета налогов в компаниях, где за них платят еще и страховые и пенсионные отчисления и где они имеют оплачиваемый отпуск. За эти деньги такой разработчик работает условные 8 часов (по факту, 6 - это обычно максимум) в день в течение 248 рабочих дней. При этом, у работодателя у него есть еще 28 дней отпуска, что уменьшает количество рабочих дней до 220, практически не уменьшая сумму его зарплаты.

    Соответственно, опытный разработчик, который разбирается в рынке, запросит как минимум 170000 * 12 / 220 = 9272 руб/день, а более уверенный в себе - все 13636 руб/день, что является вилкой от 1159 руб. до 1704 руб (или от $18 до $27) в час при 8-часовом рабочем дне (а при 6-часовом от $24 до $36), что для мирового рынка является достаточно низкой ставкой для таких услуг. Эту сумму вы можете считать некой планкой качества, ниже которой риски взаимодействия с недостаточно опытным разработчиком существенно возрастают.

    Теперь по вашему вопросу: разработчикам с такой ставкой интереснее работать на зарубежных заказчиков и продвигаться на upwork.com им, вероятно, будет комфортнее, чем работать на российском рынке. Я сам много лет работал на апворке и по себе, например, могу сказать, что на заказы со ставкой >$35 уже обращал внимание. Моя гипотеза в том, что $25-35 это нижняя планка, за которую можно найти разработчика и искать лучше именно там (хотя есть и другие биржи, но делюсь своим личным опытом) и именно там они обитают и я слышал много историй успеха (и в т.ч. был ей), где люди и компании находили очень достойных исполнителей. Важный момент только брать проверенных исполнителей с хорошими отзывами.

    Хорошая новость в том, что дешевле также можно найти сильного разработчика, но контроля ему нужно будет больше, либо какая-то из важных составляющих работы будет западать. Но всегда есть алмазы без отзывов, либо люди, кто стесняется просить больше, либо у кого плохо с английским и они готовы работать с российскими заказчиками. Тут уж как повезет и могу только пожелать удачных поисков )

    С уважением,
    Иван Томилов
    CEO of Athanor
    Ответ написан
    Комментировать
  • Как в PostgreSQL сделать уникальный идентификатор среди всех таблиц базы (глобально)?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    Здравствуйте. То, о чем вы говорите, называется GUID, но какого-то унифицированного одного алгоритма его генерации для всех нет. Но в postgres есть расширения, которые могут вам помочь и вам не придется генерить его на уровне приложения.
    Эта суперкороткая статья может быть вам полезна: https://postgrespro.ru/docs/postgrespro/9.5/dataty... и дать начальный стимул и понимание куда копать дальше. Удачи )

    Update (29.01.20):

    Сразу к делу и примерам. Сделать это можно, например, так:

    -- Начнем со схемы данных. На всякий случай проиллюстрирую как может выглядеть идея генерации guid базой, с которой
    -- я в самом начале и начал. Но вам, наверное, не будет смысла эту функцию использовать, т.к. сценарий использования
    -- у вас другой.
    --
    -- Обратите также внимание, что я поменял тип на BIGINT. У меня была гипотеза, что вы используете TEXT, т.к., возможно,
    -- не знали про BIGINT. Недостаток TEXT в том, что Postgres не построит по нему индекс и выборки по id будут все
    -- медленнее и медленнее. Я бы порекомендовал сразу отрефакторить все так, чтобы использовался именно BIGINT.
    --
    -- Все поля также назвал английскими словами, т.к. это можно считать индустриальным стандартом. NOT NULL добавил
    -- по наитию: логика подсказывает, что PRIMARY KEY у каждой таблице обязателен, как и данные. Просто для полноты.
    
    -- Начнем с функции, которая будет генерировать нам ID для примера.
    
    CREATE OR REPLACE FUNCTION guid()
        RETURNS BIGINT AS
    $BODY$
    BEGIN
        -- Количество секунд с начала эпохи Линукса и домножаем на какой-то множитель, чтобы увеличить точность
        -- и получить 1580307917143.431 вместо 1580307917.143431
        RETURN CAST(EXTRACT(EPOCH FROM NOW()) * 1000 AS BIGINT);
    END;
    $BODY$
        LANGUAGE 'plpgsql' VOLATILE;
    
    -- Теперь перейдем к самой схеме данных и создадим ее.
    
    CREATE TABLE users (
       id BIGINT PRIMARY KEY NOT NULL DEFAULT guid(),
       pseudonym TEXT NOT NULL
    );
    
    CREATE TABLE posts (
       id BIGINT PRIMARY KEY NOT NULL DEFAULT guid(),
       content TEXT NOT NULL
    );
    
    CREATE TABLE comments (
      id BIGINT PRIMARY KEY NOT NULL DEFAULT guid(),
      text TEXT NOT NULL
    );
    
    -- Для того чтобы повесить CONSTRAINT на id целевых таблиц, понадобится сделать VIEW, котоый будет содержать все
    -- id из всех этих таблиц, а также функцию, которую мы сможем использовать для CONSTRAINT. По сути, делаем то же,
    -- что делали бы на уровне приложения, но на уровне БД.
    
    CREATE OR REPLACE VIEW all_ids AS
    SELECT id FROM users UNION
    SELECT id FROM posts UNION
    SELECT id FROM comments;
    
    -- Теперь перейдем к функции, которая и будет выполнять всю грязную работу.
    
    CREATE OR REPLACE FUNCTION is_unique_id (BIGINT)
        RETURNS BOOLEAN AS 'SELECT CASE WHEN
                                       (SELECT 1
                                        FROM all_ids
                                        WHERE  id = $1) > 0
                            THEN FALSE ELSE TRUE END'
        LANGUAGE 'sql' WITH  (isstrict);
    
    -- Осталось только повесить CONSTRAINT
    
    ALTER TABLE users ADD CONSTRAINT id CHECK (is_unique_id(id));
    ALTER TABLE posts ADD CONSTRAINT id CHECK (is_unique_id(id));
    ALTER TABLE comments ADD CONSTRAINT id CHECK (is_unique_id(id));
    
    -- А теперь внимание. Теперь вам придется быть очень внимательным при добавлении таблиц, в рамках которых id должен
    -- быть уникален. При добавлении новой таблцы будет необходимо:
    --   1. Пересоздать VIEW, дополнив запрос новыми таблицами.
    --   2. Не забыть повесить аналогичный CONSTRAINT на новую таблицу.
    --
    -- Также обратите внимание, что при вставке новой записи в любую из таблиц будет проверяться весь созданный VIEW
    -- и очень важно чтобы это был не полнотекстовый поиск, а работали индексы, поэтому так важно отрефакторить все в BIGINT.
    
    -- Пришло время тестирования. Вставляем данные.
    
    INSERT INTO users (pseudonym) VALUES ('Первый');
    INSERT INTO users (pseudonym) VALUES ('Второй');
    
    INSERT INTO posts (content) VALUES ('О том как надо');
    INSERT INTO posts (content) VALUES ('О том как не надо');
    
    INSERT INTO comments (text) VALUES ('Я думаю что решение...');
    INSERT INTO comments (text) VALUES ('Хорошо я пропробую сделать...');
    
    -- И глянем что получилось.
    
    SELECT * FROM users;
    -- 1580326610797	Первый
    -- 1580326611809	Второй
    
    SELECT * FROM posts;
    -- 1580326613690	О том как надо
    -- 1580326613712	О том как не надо
    
    SELECT * FROM comments;
    -- 1580326613779	Я думаю что решение...
    -- 1580326613797	Хорошо я пропробую сделать...
    
    -- Время X: тестируем нашу проверку, пытаясь вставить в таблицу users id из таблицы comments:
    
    INSERT INTO users (id, pseudonym) VALUES (1580326613779, 'tiabc');
    -- [23514] ERROR: new row for relation "users" violates check constraint "id" Detail: Failing row contains (1580326613779, tiabc)
    
    -- Profit!


    Что хочу сказать с точки зрения проектирования архитектуры и вообще. Использование любого нового инструмента должно быть обосновано. Как правило, сложная схема БД ведет к сложностям в поддержке и к тому, что какие-то вещи забывают обновляться, в отличие от уровня приложения.

    В вашем же случае, честно говоря, схему очень сильно хочется упростить, а не усложнить. Хочется добавить поле created_at с DEFAULT CURRENT_TIMESTAMP() и хочется добавить реляционные связи. Либо же в принципе уйти на нереляционную БД и задать структуру сущностей там (что опять же должно быть обосновано).

    И дальше именно на уровне приложения делать эту выборку, т.к. это упростит поддержку кода, а с точки зрения трудозатрат ваших и БД при выборках и вставках будет то же самое.

    Помимо этого, поскольку вы делаете процессинг на основе id и его порядковых номеров, не могу не порекомендовать обратить внимание на одни из базовых принципов проектирования ПО. Конечно, вся картина не видна, но я тут вижу нарушение буквы S (Single Responsibility), что id у вас и за порядок отвечает, и за уникальность (да еще и между несколькими таблицами). Хочется как-то их развязать. Со временем жизни продукта это часто оказывается полезно.

    Опять же, беспокойств о том, что вы хотите достичь, достаточно много, но если отвечать именно конкретно на ваш вопрос и давать дополнительно какие-то рекомендации, то как-то так )

    Желаем удачи, образования и всех благ )

    С уважением,
    Иван Томилов
    CEO of Athanor
    Ответ написан
    2 комментария
  • Как убедить клиента что пора переделать проект?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    Давайте по порядку.
    1. Про «делать проще» или «закладывать архитектуру». Вы пошли по первому пути вполне закономерно, потому как зачем клиенту платить больше для решения своей задачи, если можно заплатить меньше? Для него это противоречит здравому смыслу. Поделюсь опытом, мы например работаем так всегда, но с небольшой (на самом деле ключевой) поправкой — мы планируем, как проект будет развиваться дальше и обязательно вместе с клиентом. Когда есть этот план работа строится итеративно, сначала выкатывается минимальная работоспособная версия (mvp), которая покрывает критический контур системы (то, без чего этот продукт точно не будет работать), затем v0, v1 и так далее. Мысль в том, что это нормальная практика. На счет «закладывать архитектуру», а откуда вы знаете какая она должна быть? Хватит пальцев одной руки чтобы посчитать сколько я видел клиентов, которые четко знают, что им нужно и в конце проекта что-нибудь не менялось. Зная это, как можно просчитать архитектуру или хотя бы даже желаемый функционал? Лучше идти итеративно и постепенно достраивать систему.

    2. Теперь про постоянные фичи, костыли и прочее. Тут упираемся в бизнес-аналитику и сбор требований. Нельзя работать в режиме Клиент сказал сделать фичу — Сделали фичу. Да ещё и по нескольку раз в день. Важно моделировать бизнес-процессы и встраивать их в существующую систему, вы-то, видно, это понимаете, но клиент-то — нет. Чтобы он понял это, с ним нужно общаться на его языке и строить работу от бизнеса. Задайте на брифинге вопросы «Какую цель вы преследуете, внедряя эту фичу? Как ещё можно достигнуть этой цели, может есть решение, которое вообще не потребует внедрения никаких фич, может это можно сделать бесплатно? Например у нас есть бизнес-аналитик, который, занимается только этим — задаёт нужные вопросы, объясняет последствия тех или иных решений, затем сам формулирует задачи, переведя их с языка бизнеса на язык разработки и начинается работа.

    3. По вашему тексту видно, что клиент воспринимает вашу компанию, как исполнителя, попробуйте перейти в статус партнёра. Не уверен, что это возможно на текущей стадии, работа у вас уже выстроена, но можно попробовать изменить подход на следующем проекте. Покажите, что вы продаёте не строчки кода, а решения для бизнеса, тогда отношения между вами и клиентом качественно изменятся. Ведь клиент искренне верит, что он один на один со своими проблемами и задачами, что только он и понимает как нужно и вашими руками это делает. Поэтому они и просят частых изменений и, вероятно иногда противоречащих друг другу — они ищут решение, ну так покажите, что вам можно довериться.

    4. Про, собственно, сам вопрос, как быть и как попробовать объяснить, что накопился огромный техдолг и его нужно рефакторить. Возвращаемся к тому же вопросу, с бизнесом нужно говорить на их языке. Сделайте презентацию, позовите клиента на вечерний чай (или чего покрепче) и покажите как сейчас и как могло бы быть. Покажите, что костыли и плохая архитектура замедляют разработку новых фич, что они УЖЕ переплатили вот *столько-то* и дальше будут переплачивать *вот столько-то* каждый месяц (ну хотябы приблизительно посчитайте). Покажите, что качество продукта неуклонно снижается, что в итоге всё работает медленно и из за этого они теряют клиентов вот в таких-то местах. Объясните, что да, сейчас всё работает и держится на ваших офигенно качественных костылях, но может произойти реальный крах вот *в такой-то момент*. Покажите, где для них наоборот есть точки роста, если сделать как вы говорите.

    Клиент должен быть разрабу друг и партнёр, а не мудак :)

    Надеюсь помог и вы что-то извлечёте из этого текста для себя, вообще чтобы более предметно говорить про это нужно больше конкретики :)

    P.S. Картинки — ТОП орал долго, спасибо)

    Павел Паленин
    Head of design in Athanor
    Ответ написан
    1 комментарий
  • Как работать в режиме энергосбережения?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    Есть ряд методик, помогающих справляться с нагрузкой на больших дистанциях. Одна из популярных - работа "помидорами". Суть её в том, что в течение относительно небольшого промежутка времени (25 минут) идет работа только над одной задачей, без отвлечения на соц. сети и прочие раздражители. За периодом работы следует пятиминутный отдых, за который как раз можно успеть проверить нотификации в телефоне и ответить на сообщения.
    Эта и другие полезные техники есть в книге Дорофеева "Джедайские техники". Автор там подбирает значительный арсенал, как экономить силы и время.
    Ещё на эту тему есть книга "Getting Things Done" от Дэвида Аллена, тоже даёт понимание, как "менеджерить" и справляться с собственными задачами, а не закапываться в них. 
    Ответ написан
    Комментировать
  • Как узнать первую цифру числа?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    const a = 99;
    const b = a.toString();
    console.log(b[0])
    Ответ написан
    1 комментарий