Выбор языка программирования: 3 совета от программиста из Apple

    image

    От переводчика: эта статья — перевод материала, написанного программистом Аластером Парагасом (Alastair Paragas) из Apple.Он работал с такими языками программирования, как Javascript, Python, PHP, Java, Scala, Haskell, Swift и Rust. Аластер делится собственными размышлениями на тему выбора и изучения «своего» языка, ведь этот вопрос актуален как для новичков, так и для профессионалов, которые хотят выбрать новый инструментарий.

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

    Skillbox рекомендует: Практический курс «Профессия Веб‑разработчик».
    Напоминаем: для всех читателей Хабра — скидка 10 000 рублей при записи на любой курс Skillbox по промокоду «Хабр»
    .

    Сравнительные показатели


    Уровни абстракции

    Если сильно обобщить, можно сказать, что современные языки программирования разделяются на три типа:

    1. «Быстрые», которые используются для оперативного создания приложений или их прототипов.
    2. «Инфраструктурные», которые помогают оптимизировать или дорабатывать отдельные части уже написанного приложения для того, чтобы повысить его производительность.
    3. Так называемые системные языки программирования, использование которых позволяет получить в свое распоряжение полноценный контроль над памятью устройства.

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

    Если говорить об изучении языков, то сначала стоит попробовать первый тип — «быстрые» языки: они позволяют сразу видеть результат работы и учиться на собственных ошибках. В первую очередь это PHP, Javascript, Ruby и Python. Порог входа здесь минимален, и научиться основам можно за короткий срок. У этих языков стандартные библиотеки, которые позволяют добавлять большое количество функций в приложение, а спектр их возможностей достаточно велик.

    from concurrent.futures import ThreadPoolExecutor
    from http.client import HTTPException
    from urllib import request
    from typing import Union, Dict, Any, List 
    def get_request_task(url: str) -> Union[List[Dict[str, Any]], None]:
      try:
        contents = None
        with request.urlopen(url) as response:
          contents = response.read()
        return contents
      except HTTPException:
        return None
    with ThreadPoolExecutor() as executor:
      for result in executor.map(get_request_task, [
        "https://jsonplaceholder.typicode.com/posts",
        "https://jsonplaceholder.typicode.com/comments",
        "https://jsonplaceholder.typicode.com/albums"
      ]):
        if result is None:
          print("Something terrible has happened!")
        else:
    print(result)

    Реализация многопоточных HTTP-запросов в Python со статической типизацией. Многопоточность предусматривает возможность чередования трех задач (назовем их задачами A, B и C). В то время как одна задача (скажем, задача A) выполняет некоторую операцию с привязкой к I / O (и, следовательно, не выполняет какую-либо работу по вычислению), одновременно с ней выполняются и другие задачи.

    Что касается «инфраструктурных» языков, то это Java, Kotlin, Scala, Clojure, а также GoLang, Swift и Haskell. Назвать их удобными можно с натяжкой, но зато они позволяют создавать производительные приложения. К числу сложностей относится меньшее число элементов «из коробки», точный синтаксис и т.п. Эти языки хороши тем, что позволяют производить тонкую настройку приложения. Если вам необходима скорость работы, попробуйте написать приложение на одном из них.

    import Foundation
    import Dispatch
    func getRequestTask(url: String, dispatchGroup: DispatchGroup) {
      dispatchGroup.enter()
       let request = URLRequest(url: URL(string: url)!)
      let task = URLSession(configuration: URLSessionConfiguration.default).dataTask(
        with: request,
        completionHandler: {
          (data, response, error) in
          if let data = data {
            if let dataAsString = String(data: data, encoding: .utf8) {
              print(dataAsString)
              dispatchGroup.leave()
              return
            }
          }
          print("Something terrible has happened!")
          dispatchGroup.leave()
        }
      )
       task.resume()
    }
    let requestDispatchGroup = DispatchGroup()
    for url in [
      "https://jsonplaceholder.typicode.com/posts",
      "https://jsonplaceholder.typicode.com/comments",
      "https://jsonplaceholder.typicode.com/albums"
    ] {
      getRequestTask(url: url, dispatchGroup: requestDispatchGroup)
    }
    requestDispatchGroup.wait()

    Аналогичная задача уже решалась выше при помощи Python. Теперь в деле — Swift.

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

    Функциональность

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

    module Main where
    import Control.Monad.IO.Class (liftIO)
    import Control.Monad.Trans.Resource (runResourceT)
    import Data.Conduit (($$+-), ($=+), runConduit)
    import Data.Conduit.List (mapM_, map, filter, catMaybes)
    import Data.Text (unpack)
    import Data.Maybe (fromJust)
    import Web.Twitter.Types
      (StreamingAPI(SStatus, SRetweetedStatus)
      , Status(Status), statusText, statusLang
      , RetweetedStatus(RetweetedStatus), rsRetweetedStatus
      )
    import Web.Twitter.Conduit.Stream (stream)
    -- Filters Twitter tweets that are written only in English
    filterEnglishTweets :: StreamingAPI -> Bool
    filterEnglishTweets tweet =
      let
        langIsEnglish (Status {statusLang=language}) = case language of
          Just "en" -> True
          _ -> False
      in case tweet of
        SStatus statusObj -> langIsEnglish statusObj
        SRetweetedStatus (RetweetedStatus {rsRetweetedStatus=statusObj}) ->
          langIsEnglish statusObj
        _ -> False
     -- Filters Twitter tweets that are original posts
    tweetParser :: StreamingAPI -> Maybe String
    tweetParser tweet = case tweet of
      SStatus (Status {statusText=status}) -> Just $ unpack status
      SRetweetedStatus (RetweetedStatus {rsRetweetedStatus=rstatus}) ->
        Just $ unpack $ statusText rstatus
      _ -> Nothing
    main :: IO ()
    main = do 
        -- a bunch of connection setup details to Twitter
        {-
          Imagine a stream/production line of continually incoming Twitter tweets
          out of this stream, non-English tweets are removed
          each remaining tweet then gets packaged into one of two forms
            - one for original tweets
            - one for non-original tweets (retweets and whatnot)
          We then only grab packaged forms of original tweets and display them!
        -}
        in runResourceT $ do
          stream <- stream twitterInfo connectionManager apiRequest
          stream $=+
            Data.Conduit.List.filter filterEnglishTweets $=+
            Data.Conduit.List.map tweetParser $=+
            Data.Conduit.List.catMaybes $$+-
    Data.Conduit.List.mapM_ (liftIO . putStrLn)

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

    Среда выполнения — необходимо знать, как ваше приложение будет работать на различных системах. Нужен ли языковой интерпретатор (например, Python, NodeJS, PHP)? Генерируется ли зависимый от архитектуры системы бинарник (например, Swift и GoLang)? Использует ли выбираемый язык сочетание первого и второго варианта, например, приложение компилируется и запускается на некоторой виртуальной машине (Java, Scala, Clojure)?
    Кстати, на пути к совершенству очень рекомендуется изучить и начать использовать Docker плюс обязательно разобраться с принципами администрирования Linux.

    Библиотеки — каждый язык хорошо подходит в определенных ситуациях. Например, Java соответствует множеству требований оркестровки и сетевой логистики, включая поддержку баз данных через стандартизацию интерфейса JDBC и проекты, подобные тем, которые подпадают под поддержку Apache Foundation. То же самое касается Python — он идеален для анализа данных и статистических подсчетов, — а также Haskell с его грамматиками, регулярными выражениями и компиляторами. Популярность языка и размер его сообщества — еще два аргумента, которые говорят в пользу использования определенного средства программирования в своем проекте. Если коммьюнити невелико, то на скорую развернутую помощь его участников рассчитывать не стоит. И, наоборот, чем больше сообщество и популярнее язык программирования, тем быстрее можно решить сложную задачу или получить совет от коллег.

    Garbage Collection

    «Сборка мусора» — одна из форм автоматического управления памятью. Специальный процесс, называемый garbage collector, периодически освобождает память, удаляя объекты, которые уже не будут востребованы приложениями. Каждый язык программирования выполняет это по-своему.

    Python реализует подсчет ссылок посредством алгоритма «stop-the-world». Он приостанавливает выполнение программы, запускает и выполняет сборку мусора, затем возобновляет выполнение основного процесса. В ходе «уборки» фигурирует 3 отдельных «поколения» — комплекта «мусорных куч». Нулевое содержит самые «свежие» объекты, затем идут поколения 1 и 2.

    import gc
    import ctypes
    gc.set_debug(gc.DEBUG_SAVEALL)
    class PyObject(ctypes.Structure):
        _fields_ = [("refcnt", ctypes.c_long)]
    object1 = {}
    object2 = {}
    object3 = {}
    object1['reference_to_2'] = object2
    object2['reference_to_1'] = object1
    object3['some_key'] = 1
    object1_memory_address = id(object1)
    object2_memory_address = id(object2)
    object3_memory_address = id(object3)
    print "Before garbage collection --->"
    print "Refcount for object1: {count}".format(
      count=PyObject.from_address(object1_memory_address).refcnt
    )
    print "Refcount for object2: {count}".format(
      count=PyObject.from_address(object2_memory_address).refcnt
    )
    print "Refcount for object3: {count}".format(
      count=PyObject.from_address(object3_memory_address).refcnt
    )
    del object1, object2, object3
    gc.collect()
    print "After garbage collection --->"
    print "Refcount for object1: {count}".format(
      count=PyObject.from_address(object1_memory_address).refcnt
    )
    print "Refcount for object2: {count}".format(
      count=PyObject.from_address(object2_memory_address).refcnt
    )
    print "Refcount for object3: {count}".format(
      count=PyObject.from_address(object3_memory_address).refcnt
    )
    print "Objects that cannot be cleaned up by reference counting: --->"
    for x in gc.garbage:
    print x

    Реализация garbage collector на Python


    Результаты работы программного кода, указанного выше

    В PHP (начиная с версии PHP5.3) используется еще один вариант сборки мусора наряду с подсчетом ссылок. Здесь этот процесс при необходимости выполняется вместе с программой. Подграфы, которые не могут быть достигнуты из корня, устраняются.

    Swift также использует подсчет ссылок, иных способов сборки мусора здесь нет. Ниже показывается кейс, когда «сильный» счетчик объекта целиком доходит до 0 и очищается Person (поскольку он слабо соотносится с Apartment).

    class Person {
        let name: String
        init(name: String) { self.name = name }
        var apartment: Apartment?
        deinit { print("\(name) is being deinitialized") }
    }
     
    class Apartment {
        let unit: String
        init(unit: String) { self.unit = unit }
        weak var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }



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

    Повторяющиеся концепции


    Создание пакетов и управление ими

    Знакомьтесь с механизмами хранения и отслеживания зависимостей, а также способами поддержания информации о «сборке» (описание пакета, порядок запуска модульных тестов, настройки и подготовки среды и т.п.).

    Python использует pip in tandem with a requirements.txt file для управления зависимостями и setup.py для управления настройками окружения, Haskell работает с Cabal для решения обеих задач, у Java есть Maven и Gradle, в случае Scala работает SBT, PHP использует Composer, NodeJS — npm  и т.п.

    Обязательно нужно определиться с локализацией окружения разработки — возможно, вы захотите запустить разные версии языков программирования в зависимости от проекта. Phpbrew для PHP, pyenv для Python и nvm для NodeJS дают возможность это сделать.


    Используя pyenv, можно работать с разными версиями Python.

    В частных случаях бывает так, что библиотека, использованная в одном проекте, автоматически устанавливается в другие. Это актуально, в частности, для языков вроде Python и Haskell. Чтобы избежать этой проблемы, стоит использовать virtualenv/venv для Python, virtphp для PHP и Cabal Sandboxes для Haskell.



    Асинхронный ввод/вывод

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



    const https = require("https");
    const urlList = [
      "https://reqres.in/api/users?page=1",
      "https://reqres.in/api/users?page=2",
      "https://reqres.in/api/users?page=3"
    ];
    function getSiteContents(url) {
      return new Promise(function (resolve, reject) {
        https.get(url, function (res) {
          var bodyData = "";
          res.on("data", function (chunk) {
            bodyData += chunk;
          });
          res.on("end", function () {
            resolve(bodyData);
          });
          res.on("error", function (error) {
            reject(error.message);
          });
        });
      });
    }
     // One way we can proceed with execution
    // Make one Promise out of a list of Promises
    Promise.all(urlList.map(getSiteContents))
      .then(function (siteContents) {
        console.log("Promise based execution --->");
        console.log(siteContents);
      });
     // Another way we can proceed with execution
    // "async" is an ES7 feature that makes our Promise/async I/O code look
    // more synchronous
    async function main () {
      const siteContents = await Promise.all(urlList.map(getSiteContents))
      console.log("Main() based execution --->");
      console.log(siteContents);
    }
    main();
    // As Promises will happen in some future time, this will happen first
    console.log("This console.log will most likely happen first");

    Реализация асинхронного I/O с использованием Javascript

    Функциональное программирование

    Функциональное программирование позволяет «рассказать» компьютеру на высоком уровне, что вы от него, собственно, хотите. Большинство языков сегодня имеют самые базовые возможности для реализации этого: через map, filter, reduce for lists и т.п. Но это все равно стоит использовать. Ниже показан пример функционального программирования на языке, который вроде бы не предполагает такой возможности.

    <?php
    // Accumulator gets passed around for reuse - function as a value
    $accumulator = function (
      string $accumulated_string,
      string $mapped_list_element
    ) {
      return $accumulated_string . $mapped_list_element . "\n";
    };
    // Notice how array_map, array_filter and array_reduce
    // accept functions as parameters - they are higher order functions
    $mapped_array = array_reduce(
      array_map(
        function (int $list_element): string {
          return "A list element: " . $list_element;
        },
        [1, 2, 3, 4]
      ),
      $accumulator,
      ""
    );
    echo "Mapped Array: \n";
    echo $mapped_array;
    $filtered_array = array_reduce(
      array_filter(
        [1, 2, 3, 4],
        function (int $list_element): bool {
          return $list_element > 2;
        }
      ),
      $accumulator,
      ""
    );
    echo "Filtered Array: \n";
    echo $filtered_array;
    // Closures "enclose" over their surrounding state
    // The $closure_incrementer function here returns a function
    // making it a higher order function.
    echo "Closure Incrementer: \n";
    $closure_incrementer = function () {
      $internal_variable = 0;
      return function () use (&$internal_variable) {
        return $internal_variable += 1;
      };
    };
    $instance = $closure_incrementer();
    echo $instance() . " is equal to 1\n";
    echo $instance() . " is equal to 2\n";

    Обучение


    Первый этап — поиск необходимой информации на специализированных ресурсах и создание небольшого проекта после того, как базовая подготовка пройдена. В большинстве случаев можно воспользоваться статьями вроде «Выучи Х за Y дней», многие из них весьма хороши. Во многих случаях есть интерактивные примеры для обучения: A Tour of GoLang и GoLang by example (для GoLang), NodeSchool Command Line exercises (для Javascript, а именно NodeJS), Scala Exercises (для Scala), Python Koans (для Python) и т.п.

    Начинать с чего-то сложного не стоит. Создание небольших приложений и скриптов — то, что нужно новичку. Общее количество строк кода в таких опытах не превышает 300–400. Главное, что необходимо на этом этапе, — получить базовую информацию, научиться программировать со сколь-нибудь нормальной скоростью и самое важное — понимать, что вы делаете.

    func containedClosureIncrementer() -> (() -> Int) {
        var anInt = 0
        func incrementer() -> Int {
            anInt = anInt + 1
            return anInt
        }
        return incrementer
    }
    func containedClosureIncrementer2() -> () -> Int {
        var anInt = 0
        return {
            anInt = anInt + 1
            return anInt
        }
    }
     
    let closureIncrementer = containedClosureIncrementer()
    print("containedClosureIncrementer call - should be 1: \(closureIncrementer() == 1)")
    print("containedClosureIncrementer call - should be 2: \(closureIncrementer() == 2)")
     
    var someOptionalValue: Optional<String> = nil;
    print("Optional - someOptionalValue is null: \(someOptionalValue == nil)")
    someOptionalValue = "real value"
    print("Optional - someOptionalValue is 'real value' \(someOptionalValue == "real value")")
     
    (["real value", nil] as Array<Optional<String>>).forEach({
        someOptionalValue in
        if let someValue = someOptionalValue {
            if someValue.hasPrefix("real") {
                print("someValue: has real")
            } else {
                print("someValue: doesn't have real")
            }
        } else {
            print("someValue: has nil")
        }
    })
     
    if (someOptionalValue ?? "").hasPrefix("real") {
        print("Has real 2")
    } else {
        print("Doesn't have real")
    }
     
    let numbersList: [Int] = Array(1...10)
    print("List of numbers 1 to 10: \(numbersList)")
    let numbersListTimes2 = numbersList.map({
        (someNumber: Int) -> Int in
        let multiplicand = 2
        return someNumber * multiplicand
    })
    let numbersListTimes2V2 = numbersList.map({
        number in number * 2
    })
    let numbersListTimes2V3 = numbersList.map {
        $0 * 2
    }
    print("List of numbers * 2: \(numbersListTimes2)")
    print("V1, V2 Map operations do the same thing: \(numbersListTimes2 == numbersListTimes2V2)")
    print("V1, V3 Map operations do the same thing: \(numbersListTimes2 == numbersListTimes2V3)")
     
    func testGuard() {
        let someOptionalValue: Optional<String> = nil;
        guard let someOptionalValueUnwrapped = someOptionalValue else {
            print("testGuard: Thrown exception - nil value")
            return
        }
        print("testGuard: no exception - non-nil value: \(someOptionalValueUnwrapped)")
    }
    testGuard()
    class RuntimeError: Error {}
    [{throw RuntimeError()}, {1} as () throws -> Int].forEach {
        let returnValue = try? $0()
        if let returnValueUnwrapped = returnValue {
            print("List of closures: A normal value was returned \(returnValueUnwrapped)")
        } else {
            print("List of closures: An error was thrown")
        }
    }

    Пример начального скрипта, который дает представление начинающему программисту о том, как работает его код

    Второй этап — более глубокое изучение языка, создание полноценного проекта, который уже нельзя назвать «детским». Во многих случаях необходимо знакомиться с официальной документацией. Для Javascript это Mozilla Developer Docs, для Swift — Swift Official Docs, для Java — Java Learning Trails, для Python — Python Official Docst. Особое внимание стоит обратить на онлайн-курсы с хорошими преподавателями.

    Кроме того, стоит изучить другие проекты с открытым кодом. Ресурсы вроде Annotated jQuery source или Annotated BackboneJS source дают представление о том, как конкретный язык программирования и дополнительные библиотеки используются в профессиональных проектах.

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



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

    Skillbox
    Онлайн-университет профессий будущего

    Комментарии 36

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

      Аж передернуло от такого перевода. Пользуйтесь техническим словарём, термины в ФП взяты из математики. map, кроме того что это карта, еще переводится как «отображение», а reduce правильно перевести как «свёртка», но лучше на самом деле оставить эти три слова без перевода, ведь это устоявшиеся названия операций над списками
        +2
        Можно вставить мои пять копеек по выбору ЯП? Выбор языка программирования это как выбор женщины, представьте вы вошли в бар «Последний приют» и перед вами стоят: Быстрая, Красивая, Словоохотливая, Румяная, Одноглазая, Молодая, Силиконовая, Добрая, Транжира, В очках, Гламурная, Мутантка, Старая, Ведьма выберите свой идеал и вы поймете какой язык программирования вам нужен.:-)
          0
          Ну очевидный выбор-то, «силиконовая» (ака кремниевая) же))
            0
            silicone != silicon
            0
            Быстрая Румяная Мутантка. Очевидно же!
              +2
              С++?
                –1
                C + 1C + Objective-C
                  0
                  Румяная это Rust ещё может быть.
                  0

                  Старая Гламурная Ведьма В очках)))

                    0
                    Ассемблер + Swift + Perl + Python
                  0
                  Хорошо, что не под цвет галстука. Что важно: блуд здесь идет только на пользу.
                    0
                    Умной нет?
                      0
                      Они все умные, в разной степени конечно.:-) Точнее каждая по своему:-)
                        0
                        Вы пришли не первым.
                        0
                        можно расшифровать, что они означают? C# например кто?
                          0
                          Добрая
                          0
                          По хорошему ЯП выбирается под задачу, т.к. это всего лишь инструмент. Программисты не должны так сильно привязываться к одному языку
                            0
                            А хаскель кто из них?
                              0
                              Ну такой там нет.
                            +2
                            Я сильно извиняюсь, но не понял, что есть «три совета»?
                              0
                              Есть 3 языка: объектный, функциональный и С++ (возможно Раст тоже, но я только по наслышке). Все остальное детали реализации.
                                0
                                Есть еще стековый, вроде Forth :)
                                0
                                В какой класс отнести лисп, в какой класс отнести какой-нибудь из современных ML-подобных, и насколько они близки друг к другу?
                                  0

                                  Лисп — функциональный вроде. Но еще пролог есть.

                                    0
                                    Пролог не функциональный. Пролог (англ. Prolog) — язык и система логического программирования, основанные на языке предикатов математической логики дизъюнктов Хорна, представляющей собой подмножество логики предикатов первого порядка.
                                      0

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

                                +1
                                А где языки платформы .net, или аппле конфликтует с Microsoft?
                                  0

                                  В Apple как и в других компаниях, у каждой команды свой стек технологий, просто конкретно в этой .Net не используют.
                                  Автор статьи — это инженер первой категории после стажировки, судя по технологиям он скорее всего работает в отделе IS&T (Information services and technology). Как раз там можно встретить PHP, jQuery и другие подобные штуки.

                                  +3
                                  Человек, который поймет этот текст сам разберется, какой язык ему нужен
                                    0
                                    Интересно будет кому-то и ли нет, но я опишу свою историю выбора ЯП:
                                    • В школе преподавали Basic, на нём и писал программы в рамках школьной программы.
                                    • Курсы оператора ПК: Clipper, Pascal. Выпускная работа была написана на Clipper.
                                    • Университет: С++, Ассемблеры. Позже не использовал никак.
                                    • 1-е место работы: HTML, PHP, SQL по собственной инициативе.
                                    • Текущее место работы: в обязанности входит поддержание и развитие кучи легаси, написанного на: Clipper, C++ Builder 6, VisualBasic 6.0, Delphi. Периодически читаю учебники по C#, Java, JavaScript, но увеличивать уже существующий зоопарк особого желания нет. Есть идея переписать всё, но это нереально и бессмысленно.

                                      0
                                      И давно текущее место работы? Нет опасений, что рано или поздно это всё закончится по каким-то причинам и надо будет искать новую работу на открытом рынке? Навскидку вакансий по Clipper, C++ Builder 6, VisualBasic 6.0, Delphi практически нет.
                                        0
                                        На текущем месте третий год работаю.
                                        Нет опасений, что рано или поздно это всё закончится по каким-то причинам
                                        по Clipper, C++ Builder 6, VisualBasic 6.0 можно сделать вывод, что предприятие работает уже не первый десяток лет.
                                        Навскидку вакансий по Clipper, C++ Builder 6, VisualBasic 6.0
                                        Было бы странным, если бы они были. Про Delphi такого не скажу.
                                          0
                                          можно сделать вывод, что предприятие работает уже не первый десяток лет.

                                          Можно сделать и другие выводы, например, что недавно одно предприятие было (или скоро будет) как неконкурентное (в том числе и из-за консервативности в области ИТ) поглощено другим и вполне реалистичен переход на стандарты (в том числе в области ИТ) новых хозяев. Часто это сопровождается массовыми увольнениями, как их не называй, сокращениями, оптимизациями или ещё как. Ну или просто решат купить "коробку" или облачное какое решение.


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


                                          Было бы странным, если бы они были.

                                          Ну вот решите вы всё-таки чему-то новому научиться и уйдёте куда-то где новые навыки более востребованы — и появится минимум одна :)

                                          0
                                          Delphi всё ещё встречаются — объём кодовой базы проектов (вполне живых и развивающихся) такой, что попытки переписать на чём-то современном обычно стухают в начале (добавить туда часто отсутствие документации по многим моментам из-за древности и, зачастую, институтского проекта 20+ летней давности как затравки этого монстра), да ещё часть команды соттвутствуют возрасту проекта и не готовы к новому. Тут должно сильно припереть для перехода, да ещё и ресурсы в наличии не помешают (денежные и временнЫе). Переход с Clipper, VB, Builder, файловых БД(dbf/pdx) был легче из-за меньших объёмов и меньшего срока жизни к моменту перехода (и то он происходил несколько лет). Выкатить замену за пару месяцев зачастую нельзя, а пользователи не очень хотять делать пол-работы в старом софте, а половину — в новом, начальство же подобных контор не готово вкладываться в долгую перспективу, «как полгода не будет видно результата? А за что мне платить тогда?».
                                        0
                                        Асинхронный != многопоточный. Весь абзац про асинхронность порезан и некорректен.

                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                        Самое читаемое