• С вами. Говорит. Робот.

    Бесит, когда робот озвучивает фразы, составленные из кусочков. В такси:

    К вам подъезжает – Ауди – черный – номер – ноль – три – четыре.

    В Сбербанке:

    Талон с номером – сто – двадцать – один – Б.

    Провалы между словами режут слух. Люди говорят связно. А. Не. По. Одному. Слову.

    Беда с произношением текста в том, что фразу нельзя собрать по кусочкам, как цепочку байт. Наивный программист уверен, что если список строк легко соединяется в одну:

    strings = ["Талон номер", "сто", "двадцать", "один"]
    " ".join(strings)
    >>> Талон номер сто двадцать один
    

    то и аудио-файлы достаточно склеить, чтобы получить результат.

    Конечно, нет. Странно, что об этом забывают в процессе развертывания системы.

    Правильно сделать так – начитать как можно больше фраз, чтобы не было пауз. Например, для чисел записать варианты от 0 до 999. Да, потребуется время, ну и что? Займет два лишних часа, зато будет классно звучать. Если в системе десять окон, то записать десять фраз:

    • Пройдите в первое окно
    • Пройдите во второе окно

    и так далее. Намного человечней, чем “Пройдите в окно. Три.”

    Так вот, в воронежском ГАИ фразы звучат именно таким образом. На каждое окно отдельная фраза. Каждой цифре – своя дорожка. Удивительно!

    Клиент с номером. Сто двадцать три. Пройдите в пятое окно.

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

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

  • Practical task

    I’m looking for a new job these days. I have had a set of interviews so far. One time, I faced an interesting practical task. In this post, I want to discuss a way how I’ve solved it.

    The original text took two pages of A4 because of lots of unuseful details. Let me retell the task by memory.

    Imagine you have a surface of Mars divided by cells. A robot moves on it. The robot knows its position represented by (x, y) coordinates and orientation (North, East, South, West, or just N, E, S, W). You may pass a command to the robot. A possible command might be to turn left (L), to turn right (R) and to move forward (F). When the robot rotates, it just changes its orientation without moving across the cell. When it moves forward, it changes the coordinates but keeps the orientation.

    A robot will be lost when goes out of the surface. But before it has been lost, it marks the last cell it stands as “dangerous”. When another robot steps on a dangerous cell, it cannot be lost. Instead, it ignores all the commands that lead to being lost.

    So you have a surfacers dimensions and a bunch of robots. For each robot, you know its initial position, orientation and a list of commands. Write a program that moves the robots in series. For each robot, print its final position and orientation. Also, if the robot has been lost, add “LOST” to that line.

    Input sample (don’t pay attention at the comments):

    5 3             # world's width and height
    1 1 E           # x, y and orientation of the first robot
    RFRFRFRF        # and its list of commands
    
    3 2 N           # the second robot pos and orientation
    FRRFLLFFRRFLL   # and its list of commands
    
    0 3 W           # the third one
    LLFFFLFLFL      # ...
                    # etc...
    

    Expected output:

    1 1 E
    3 3 N LOST
    2 3 S
    

    I know what are you thinking about. Let’s have a class World and a class Robot. Than I’ll make an instance of class Game and will move the robots across the world. But this way seems a bit weird to me because we start with the entities but not the process.

    All these entities do not say how to organize them. We have a common “Paper and Pen” problem. How should I connect my entities: Pen.writeOn(Paper) or Paper.writeWith(Pen)? Which OOP pattern says directly what are the best practices? SOLID, Composition or whatever? Should I store an array of robots inside a World instance or just keep a reference to the world in each robot? What if a robot has been lost? Should I null that reference?

    See, we’ve got lots of questions, but none of them move us to the solution.

    Moreover, once you have solved the task using classical OOP, it would be difficult to port you code to such FP languages as Clojure or Haskell, because they do not have classes, just types. Even a Javascript programmer may face some problems because JS has its own OOP system that differs from Python/Java.

    So let me provide a simple solution that do not use classes and tend to be functional. I will do it using Python, not Clojure to make it clean to everyone.

    One famous programmer said: when somebody asks me on an error in their code, I always respond the following. Imagine your program is a black box. What data does it receive? What data does it return? Once you know the answers, it becomes easy to find a mistake in logic.

    I’d like to follow this way. Let’s abstract from the entities and OOP patterns. We read parameters from the input, process them and print on a screen. So here is a scheme:

    read_params() --> play_game() --> print_results()
    

    Note that reading and writing functions do IO, so they are not pure. Instead, play_game accepts plain data structures and has no side effects. That makes it easy to test in both REPL and unit tests.

    When we have basic knowledge of the task, it’s time to write some code. The following skeleton shows my decision as well:

    
    def parse_input(source):
        ...
    
    
    def play(params):
        ...
    
    
    def compose_results(results):
        ...
    
    
    def main():
        params = parse_input(sys.stdin)
        results = play(params)
        print compose_results(results)
    
    
    if __name__ == "__main__":
        main()
    
    

    On the next step we need to clarify what data exactly we expect inside play function. There should be world’s limits for each dimension first. And we also need robots’ data.

    def play(w, h, routes):
    

    Because parsing input stream is boring, let’s just return a sample data from parse_input for now:

    
    def parse_input(source):
        return (10, 20, (
            (0, 0, "N", "LLLL"),
        ))
    

    Now let’s look closer to the play function. It’ a black box for now, but we could imagine what is behind it. I believe, it should build the world at first. Then, we iterate on robots to process them in series. That is an outer cycle.

    For each robot, we evaluate a bunch of commands: rotate left/right or move. That’s an inner cycle.

    For each robot, we collect its final state with a special flag that indicates whether a robot has been lost.

    Looks simple, let’s code that logic:

    def play(w, h, routes):
        world = make_world(w, h)
        results = ()
    
        for (x, y, ori, steps) in routes:
            robot = make_robot(x, y, ori)
            for step in steps:
                ok, world, robot = update(world, robot, step)
                if not ok:
                    break
            results += ((ok, robot), )
    
        return results
    
    

    Now we have some undefined functions that we must populate before launch the code. What we should know about the world? Only width, height and what cells are marked as dangerous. So let the world will be a tuple something like (20, 30, ((2, 3), (15, 34))).

    The following function returns a new world ready to play:

    def make_world(w, h):
        return w, h, ()
    

    Next, what data represent a robot on every turn? Both x and y coordinates plus orientation. In Python, let it be just a tuple like (1, 5, "N") or (10, 5, "S") for example.

    The function below is to create a new robot:

    def make_robot(x, y, ori):
        return (x, y, ori)
    

    Our update function takes the world, the robot and a command to evaluate. It returns a tuple of three values. The first one is a boolean flag that indicates whether the robot is still on the surface. You see, if we get False, that will mean the robot has gone away from the world and there is no reason to continue evaluation for that robot.

    The second and the third values are the new world and robot. The thing is I return new values instead of changing the previous ones. Avoiding state gives us more freedom in data manipulation. I’ll cover more on that a bit later.

    Moving on to the update function. First, we need to distinguish the command whether it is to rotate or to move a robot. The case of rotate is really easy to handle:

    def update(world, robot, step):
    
        if step in "LR":
            return True, world, rotate_robot(robot, step)
    
        # to be continued
    

    The code above clearly shows we can rotate a robot without any limits. Notice I return a new rotated robot instead of changing the one I have in my function. The rotate function is simple:

    def rotate_robot(robot, step):
        x, y, ori = robot
    
        rules = {
            ("N", "L"): "W",
            ("N", "R"): "E",
    
            ("E", "L"): "N",
            ("E", "R"): "S",
    
            ("S", "L"): "E",
            ("S", "R"): "W",
    
            ("W", "L"): "S",
            ("W", "R"): "N",
        }
    
        new_ori = rules[(ori, step)]
        return x, y, new_ori
    

    Important thing is having a dict of rules is much better then a bunch of nested ifs. It’s possible to add new rotate rules at any times as well.

    The movement forward command is a little bit trickier because of complicated logic. First of all, we need to know whether the robots stands on a dangerous cell. Wee also need to check whether the next turn will move the robot out from the surface.

    Remember, if it’s a dangerous cell at the moment, the bad movement is just ignored. If it isn’t, move the robot. And if it has lost, mark that cell as dangerous. I’ve finished with the following code:

        # continues the `update` function
    
        if step == "F":
    
            robot_next = move_robot(robot)
            placed_next = is_placed(world, robot_next)
    
            if is_scent(world, robot):
                if placed_next:
                    return True, world, robot_next
                else:
                    return True, world, robot
    
            else:
                if placed_next:
                    return True, world, robot_next
                else:
                    return False, set_scent(world, robot), robot
    

    A tree of nested ifs looks ugly to me and may be reduced. But it’s not so important at the moment. The key feature of the code above is kept in a line:

    robot_next = move_robot(robot)
    

    Remember, I spoke about immutable data structures. Instead of moving the robot, we’ve got a new one that stands on the next cell. You see? Dealing with immutable data gives us opportunity to have objects in the future. When I have a future state of a robot, I can easy check whether its state fits to the requirements. And if it does not, just return an old copy of a robot.

    Now imagine I had a mutable object with x and y fields. To check that robot, I would move it forward, e.g. change its state, than check the conditions and rollback the state if they failed. Should I say that way is weird and tends to a buggy code?

    Here is a code for the missing functions. To move a robot:

    def move_robot(robot):
        x, y, ori = robot
    
        rules = {
            "N": (0, 1),
            "W": (-1, 0),
            "S": (0, -1),
            "E": (1, 0),
        }
    
        dx, dy = rules[ori]
    
        return (
            x + dx,
            y + dy,
            ori,
        )
    

    To check if a robot is on the surface:

    def is_placed(world, robot):
        w, h, _ = world
        x, y, _ = robot
    
        return (
            0 <= x <= w
            and 0 <= y <= h
        )
    

    And to check whether a robot stands on the dangerous cell:

    def is_scent(world, robot):
        _, _, scents = world
        x, y, _ = robot
    
        return (x, y) in scents
    

    So we’ve done with the solution. What wee are missing for now are parsing input data and formatting the game result functions. I’m not sure they are so important to place them here. Check the Git repo if you are interested.

    Let’s check the data sample given in the task. Say we have sample.txt file with the input:

    5 3
    1 1 E
    RFRFRFRF
    
    3 2 N
    FRRFLLFFRRFLL
    
    0 3 W
    LLFFFLFLFL
    

    We perform:

    cat sample.txt | python mars.py
    1 1 E
    3 3 N LOST
    2 3 S
    

    Just as the expected results provided in the task.

    To sum up, I’ll list the key points that I’ve got from that task.

    1. Try to think on the program like a black box, that accepts some data and produces the result. Your mission is to uncover that black box.

    2. Use plain data structures like hash-tables, lists or tuples rather than classes and objects. Just because it keeps you from all that OOP complexity.

    3. Keep data immutable while it is possible rather then modify them.

    4. Don’t be afraid of implementing you logic with undefined functions. Or functions that return hard-coded result. You may correct them later.

    5. Separate functions that don’t have side effects from those that have. They become really easier to test.

    Find the whole code on Github. And by the way, it’s my first post written in English. I hope you enjoyed that reading.

    Thanks!

  • Хром

    Когда число вкладок в мобильном Хроме переваливает за 100, Гугл тонко троллит смайликом:

    smile

    Это не я такой упоротый, если что. Просто каждый раз, когда переходишь в браузер из другого приложения, открывается новая вкладка.

  • Почему социальные сети -- рак

    Я не понимаю, как люди пользуются социальными сетями. Перепробовал и Фейсбук, и Твиттер. Постил что-то в Гугл-Базз – вы его, наверное, и не вспомните. Листал Гугло-плюс. Не обошел вниманием отечественные ВКонтакт и Одноклассники. И каждая сеть – это полный пиздец и мрак. У меня полное непонимаение того, как это говно работает и привлекает деньги.

    Наверное, схожее чувство испытал бы сельский рабочий перед картиной Пабло Пикассо – это, блеять, что? Давайте разберемся, что не так с социальными сетями.

    Сети максимально закрыты от сторонних посетителей. Анонимному посетителю вставляют все палки, чтобы он поскорей зарегистрировался. Иначе нет навигации и поиска, сплошные попапы.

    В соцсетях пользователь пребывает в информационном пузыре. Когда ты новый пользователь, сеть не знает, что тебе нужно, и обучается наивным Байесом. Показывают Путина и котенка. Если тыкнул в котенка, больше Путина не покажут. Вырваться из пузыря невозможно.

    При малейших трениях сети кладут на пользователей болт. Никто не будет разбирать случай угона аккаунта или несправедливого бана. У сети 1М хомячков. Хомячок с номером (1М + 1) никого не интересует.

    Только здесь и сейчас. Каждая сеть показывает только то, что происходит сейчас. Отыскать публикации пятилетней давности нереально не только из-за уродского интерфейса. Сети официально подтверждают, что выкидывают из индекса старые записи.

    Дикие требования к пользователям. Требуется раскрыть много персональных данных. В любой момент сеть может заподозрить обман и требовать подтверждение телефона или емейла. Если вы не знали: чтобы удалить аккаунт из Одноклассников, нужно прислать нотариально заверенную копию паспорта.

    Любая социальная сеть – смесь рекламы и пропаганды. Ни одна публикация не появляется просто так. Ее оплачивает либо бизнес (реклама), либо государство (9 Мая, патриотизм).

    Обратите внимание, что доля публикаций, сделанных обычными пользователями, ничтожно мала. Откройте страницу любого юзера. Его лента состоит только из репостов. То есть даже в условиях, когда требования к контенту ничтожно малы, пользователи тупо репостят. Пропускают через себя говно, как черви.

    Интерфейс. Покажите сеть с нормальным интерфейсом. Я не знаю такой.

    В модераторах сидят дуболомы, которым запрещено думать. Фотография голой девочки, спасающейся от напалма, оказывается, может трактоваться как призыв к педофилии.

    Часто соцесеть вырастает из одной раковой идеи. Давайте писать короткими фразами! Так появился Твиттер. Создали ограничение, теперь героически пытаются его превзойти.

    Например, один придурок задает в Твиттере вопрос, на который нельзя ответить да или нет – требуется развернутый текст. Как же я его, блядь, умещу в 140 символов, ты подумал? Почту Твиттер не выдает. Оставить ее в вопросе автор не догадался.

    Видел, как некоторые публикуют длинные высказывания в Твиттере, но как! Они бьют текст на серию твитов по 140 букв каждый, а потом постят в обратном порядке, чтобы в ленте они шли подряд! В обратном, Карл! Это же явно либо какой-то скрипт, либо ручная работа на час.

    Пафос. Что Дуров, что Закерберг любят закатывать пафосные обращения о том, как они меняют мир. Брехня! Мир меняют открытые и децентрализорованные технологии, которые работают прозрачно и принадлежат всем. Это интернет, электронная почта, торренты, Биткоин. Но никак не поделка, где пользователя в любой момент может прихлопнуть модератор-дебил без объяснения причин.

    Сеть аппелирует к низким потребностям человека. В сети каждый желает снискать свои 15 минут славы в виде смешной картинки или наплыва лайков. Попытки заполнить собой информационное пространство выглядят жалко и выдают нужду.

    Сети делают людей несчастными. Соцесть – место, где публикуют только лучшее, но не говорят о неудачах. Какая-нибудь девушка выглядит в Инстаграме как королева, а на самом деле живет с мамой за МКАДом, ездит в электричке по 4 часа в сутки, по уши в кредитах и вообще без планов на будущее. Другие, видя только успех своих друзей, впадают в комплексы и депрессию, думая, что одни такие неудачники.

    Сети – источник постоянных отвечений. Страшно подсчитать, сколько часов вы потеряли, прокрастинируя в смешных пабликах Вконтактика. Сколько раз проверяли зря телефон, не поставил ли кто из друзей лайк?

    Интеллект среднего пользователя сети стремится к уровню 12-летнего ребенка. Сети созданы для молодых, пожилые вынуждены принимать правила. Когда я вижу, как преподаватель за сорок постит с детскими ошибками и смайликами, хочется разбить ему лицо.

    Социальные сети – необходимое зло. Бывает, никак иначе не связаться с человеком. Но блин, как же все это достало. Безумие и мракобесие.

  • Маньяки

    Самое главное в споре – вовремя определить, имеешь ли дело с адекватом или маньяком. Маньяками я называю тех, кто фанатично уверовал в превосходство той или иной технологии и не считается ни с какими доводами. У таких людей один аргумент – вера. Пока человек придерживается веры, он игнорирует любые аргументы. Пусть считает себя правым, что нам с того?

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

    Чем он лучше религиозного фанатика или коммуниста?

    Существует единственно верный способ проверить маньяка. Попросите его дать ссылку на рабочий проект, написанный на Хаскеле. Вы должны либо перейти на сайт, либо скачать утилиту. Короче, отклик должен быть мгновенным, либо есть, либо нет.

    Если маньяк кидается ссылками на Гитхаб, то он балабол. Потому что написать код, пусть даже на Хаскеле – это меньше половины дела. Гораздо больше уходит на деплой, обновления, сборки, поддержку. Если проект хоть немного популярный, вам начнут писать пользователи, им придется отвечать. Придется мониторить сервер, настраивать автозапуск, уведомления на почту при аварийных ситуациях.

    Рассуждать о технологии имеет право только тот, у кого крутится рабочий проект.

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

    Мы все читаем книжки и любим почесать языком. Но, ребят, знайте меру. Задачки с Эйлер-проджект или Экзорцизма не доказывают ничего. Точнее, они подтверждают, что в какой-то момент у автора было свободное время и упорство, чтобы посвятить себя этому занятию.

    Например, я прочитал книжку о Скале, но предпочитаю не говорить о ней, хоть и писал простейшие примеры. Нет опыта в продакшене. Любой специалист меня застыдит, реши я хоть сто задач на Скале на поиск палиндрома или вроде того.

    Маньяк зыбывает очевидную вещь. Целое больше суммы деталей. По отдельности обращаться к базе, отдавать джейсон и писать логи легко. Гораздо труднее делать это в рамках единого приложения. Когда сначала идешь в кэш, если нету, запрос в базу, кладешь в кэш, отдаешь джейсон и на каждом шаге логируешь потенциальные исключения. И сразу становится сложно, неважно Хаскель у тебя или Питон.

    В итоге выясняется, что ничего кроме задачек и мертвого проекта, которого нет в продакшене, маньяк не может предоставить публике. Только Громкие Заявления. И зарплату он получает за какой-нибудь Перл или С++. Все понятно.

    Мудрый человек – это человек сомневающийся. Когда нет никаких сомнений в превосходстве чего-то или кого-то, срочно меняйте точку зрения. Мы просто хотим избавиться от выбора, снять ответственность за ошибочное решение. “Мы берем технологию Х, потому что это убер-круто” – большая ошибка.

    Словом, взялся говорить за Хаскель, Эрланг, ФП, ООП – покажи проект. Чтобы я увидел прямо сейчас. Не трать наше время.

  • Бизнес как игра

    cover

    Еще одна отличная книжка. Авторы – те мужики, кто сделали Мосигру. Напомню, Мосигра знаменита тем, что не только продает замечательные игры для детей и взрослых, но и имеет к этому особый подход.

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

    Отдельно отмечу, что книга написана кратко, вернее – плотно. Глава может состоять из одного параграфа, где высказана простая и убедительная мысль. КПД книги стремится к единице. Вместо воды, повторов и прохладных историй читатель получит пачку мощных тезисов с короткими доказательствами их работы.

    Порадовало, что некоторые правила авторов совпадают с моими. Например, только почта для переписки. Меседжеры отвлекают внимание и снижают продуктивность. Интересен подход к смотру помещений. Приведу по памяти цитату из книги:

    В начале поиска у вас нет опыта. Поэтому на первых порах вы ищете не для того, чтобы заполучить лучшее предложение, а чтобы набраться опыта. Первые 10 помещений вы смотрите, чтобы понять, что вам вообще нужно. И ни за что не берете, даже если вариант фантастический. Только с опытом становится ясно, по каким критериям нужно отбирать на самом деле.

    Полезный принцип, взял на вооружение. Теперь, когда нужно выбрать что-то, о чем я не имею представления, смотрю первые 10 раз только чтобы увидеть разнообразие ряда.

    Другой принцип о том, как правильно делать работу:

    Предположим, директор звонит дизайнеру из другого города и требует подготовить дизайн листовки. Выслал текст, обсудили основные положения. Затем директор садится в самолет и летит в другой город. А дизайнер забыл спросить, будет ли листовка одно- или двусторонняя. Что делать?

    Здесь становится ясно, хороший ли сотрудник этот дизайнер или нет. Плохой будет ждать три часа прилета директора, чтобы перезвонить и выяснить. То есть пропинает время зазря и будет отпираться – мне не сказали.

    Хороший дизайнер сделает два варианта: с одной стороной и двумя. Когда директор прилетит, понадобится 10 минут, чтобы скомпоновать финальный результат.

    Словом, классная книженция, бодрая, полезная, читается запоем. Даже думаю бумажную версию прикупить, чтобы время от времени черпать идеи.

  • Девятая встреча

    Провели девятый митап!

    cover

    Что у нас нового. Во-первых, перенесли мероприятие в центр города. Теперь это паб Ту Даблин на проспекте Революции. Добираться стало в разы проще.

    Во-вторых, я купил петличный микрофон. После программного подавления шума получается вполне хороший звук.

    Было два доклада. Первым выступил Евгений Рыжков с темой о качестве разработки.

    Слайды

    Юрий Хрусталев продолжил с темой о поиске удаленной работы.

    Слайды

    Напомню, сообщество тусит в группе Фейсбука.

    Скоро анонс десятой, юбилейной встречи. Хотите выступить? Пишите в группу.

  • Отвечайте!

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

    Договариваюсь с человеком о сделке. Ему сорок, женат, двое детей. Предварительно обговорили детали. В назначенное время человек пропадает с радаров. После нескольких неудачных звонков все-таки берет трубку и начинает плести гнилые отмазки. Становится ясно, что он решил поднять цену, но – я пишу и сам не верю – просто ссыт сказать об этом прямо. Он решил кинуть потенциального клиента и найти нового, лишь бы не говорить об этом.

    Взрослый человек, а ведет себя как написавший в раздевалке малыш.

    Другой случай. Понадобилась одна бытовая мелочь, нашел продавца на сайте объявлений. Договорились. Не явился, не позвонил, трубку не берет. Догнал с другого номера, признался, что продал. Разве не мудак? Пусть продал, имеет полное право, по почему не берет трубку?

    И так постоянно. Один не пришел, другой не перезвонил.

    С перепиской по почте вообще беда. Задашь простой вопрос, а отвечают неделями. Например, есть ли вакансии в фирме или нет. Как можно отвечать две недели? Работа или есть или нет, а если не знаешь, то так и пиши или перенаправь.

    Вот я написал одному человеку, он отвечает, что занят. Однако регулярно публикует бред в Фейсбуке. Уже неважно, что он ответит. Работать с ним я не буду однозначно.

    Немножко выводов

    Если вы поступаете как в примерах выше, то ведете себя как мудак. Срочно прекращайте так делать. Молчание и попытка избегнуть контакта ни к чему хорошему не приводит.

    Сегодня же перезвоните и отпишите всем, кого вы так регулярно динамили. Объясните причины, назначьте новые встречи. Просто откажитесь от сотрудничества, если не подходят условия.

    Если вы договорились о сделке, но потом передумали – звоните. Просто скажите: извините, планы изменились. И все. Если решили не идти на встречу – тоже звоните. Просто скажите нет, как учат в правильных книгах.

    Если получили письмо, отвечайте в течении суток, даже если не можете помочь. Не молчите. Если сможете ответить только через две недели, попросите собеседника пингануть вас еще раз через этот срок.

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

  • Слоеный интерфейс

    Разработчики, пожалуйста, перестаньте делать интерфейс с тулбарами. Это ужасно. Тулбары остались в нулевых. Не стоит тащить их в современный веб.

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

    layer

    Когда я вижу подобное решение, хочется отбить дизайнеру пальцы газовым ключом. Очевидно, что мы потеряли треть экрана на слои. Посмотрите, сколько дырок между кнопками. Не все же сидят за 27-дюймовыми Деллами!

    Отдельный слой для виджета календаря – это вообще жесть.

    Если тупо схлопнуть слои, их останется максимум два. Далее, если мы растягиваем браузер по ширине, то все слои тоже растягиваются, но место справа остается пустым. Поэтому должен быть один слой, а кнопки будут перетекать, пытаясь занять все доступное пространство.

    А самый терминальный случай, за который положен расстрел, это когда слой залипает наверху при перемотке. Вырезается Ад-блоком.

  • Двоемыслие

    Столкнулся с каноничным двоемыслием, прям в точности по Оруэллу.

    Человек на страницах блога утверждает, что США – враг. В личной переписке выясняется, что он работает удаленно на фирму, расположенную – сюрприз! – именно в США.

    При этом фирма – не стартап с модными футболками, а системы управления на производствах. Обслуживают заводы, порты, станции, сети.

    Я теряю ориентиры – как надо самого себя обманывать, как выворачивать мозги, чтобы одновременно стыковать в голове оба факта. Работодатель – враг. Ты на него работаешь. Не клинит?

    Понятно, что все дело в деньгах. Долларовая зарплата удаленщика в США – это 2-3 зарплаты московского специалиста. Для жителя провинции, мягко говоря, очень неплохо.

    Забавный момент: деньги могут не только затуманить мозг, как это часто бывает с ментами или чиновниками, но и протрезвить. Получая такую зарплату, человек напрочь забыл о том, что США – враг. Или, по крайней мере, больше так не пишет.

    Конечно, лучше изначально принять тот факт, что врагов в политике, как и в личной жизни, не бывает. Не дожидаясь удаленки в США.

Страница 28 из 51