Каков наиболее эффективный способ добавить функциональность к незнакомую, структурно необоснованному коду? [Дубликат]

    

У этого вопроса уже есть ответ:

    

Вероятно, это все, с чем рано или поздно придется столкнуться во время разработки.

У вас есть существующий код, написанный кем-то другим, и вы должны расширить его для работы по новым требованиям.

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

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

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

Кто-нибудь знает лучший подход или название методологии, которая должна использоваться в таких случаях?

115 голосов | спросил Coder 16 FebruaryEurope/MoscowbThu, 16 Feb 2012 05:30:40 +0400000000amThu, 16 Feb 2012 05:30:40 +040012 2012, 05:30:40

8 ответов


135

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

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

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

Итак, сначала вам нужно подавить начальный ответ WTF. Скажите себе, что система работает достаточно хорошо и достаточно долго, чтобы она стала вашей проблемой, поэтому должно быть что-то хорошее.

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

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

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

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

ответил James Anderson 16 FebruaryEurope/MoscowbThu, 16 Feb 2012 10:44:01 +0400000000amThu, 16 Feb 2012 10:44:01 +040012 2012, 10:44:01
46

В принципе, у вас есть три подхода:

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

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

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

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

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

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

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

    • Пример 2: ваш клиент говорит вам, что его опыт работы с предыдущим разработчиком был катастрофой. Продукт не работает даже половину времени и полностью непригоден, как есть. Клиент понимает, что изменение исходного кода настолько плохо написанного может привести к еще большему количеству ошибок.

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

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

    • Есть части кода, которые искусно выполнены,

    • Вы узнаете много вещей из кода, который вы реорганизуете.

    • Даже при временном давлении и низких бюджетных ограничениях код более опытного разработчика по-прежнему является кодом определенного уровня. Это означает, что если есть трюк, чтобы заставить приложение работать в некоторых случаях опытный разработчик все равно оставит вам комментарий, объясняющий, почему была добавлена ​​следующая строка кода.

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

В общем, выполните следующие два правила:

  • Правило 1: лучшие разработчики, которые написали код, тем больше рефакторинг и переписывают с нуля должен использовать.

  • Правило 2: Чем больше проект, тем больше рефакторинг и переписывается с нуля, вы должны использовать.

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

ответил Arseni Mourzenko 16 FebruaryEurope/MoscowbThu, 16 Feb 2012 06:26:41 +0400000000amThu, 16 Feb 2012 06:26:41 +040012 2012, 06:26:41
40

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

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

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

Документ, как наркотик, и вы зависимы от него. Я не хочу писать роман в вашем коде, но, скорее всего, код не слишком документирован, так что это стиль .NET или Doxygen: что делает функция, ввод, вывод (и, если нужно, какие глобальные переменные или свойства, которые вы используете или меняете).

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

ответил MPelletier 16 FebruaryEurope/MoscowbThu, 16 Feb 2012 07:05:14 +0400000000amThu, 16 Feb 2012 07:05:14 +040012 2012, 07:05:14
14

При работе с Big Ball Of Mud , единственным эффективным подходом ко мне был получить (больше) тестеров и организовать тщательный профессиональный контроль качества в проекте .

  • Дело в том, что все подходы, применимые с лучшей кодовой базой, просто не сокращают его.
    С хорошим кодом я изучаю документацию, чтобы узнать о принципах, прочитать код (и особенно с помощью модульных тестов) до рисуйте детали конструкции, напишите мои собственные модульные тесты, чтобы покрыть изменения, которые я делаю и т. д. Когда код улучшен, модульные тесты остаются нетронутыми или проходят незначительные изменения, документы не нуждаются в какой-либо серьезной доработке и т. д. и т. д.
    Â
    С плохим кодом все кажется совершенно противоположным. Информация, которую я получаю от документации, - это в основном неясные и устаревшие отходы. Чтение кода просто тает мой мозг с запутанным потоком управления и контр-интуитивно понятными патчами, примененными к более ранним патчам, примененным к более ранним исправлениям, сделанным давно, чтобы быстро обойти ошибки проектирования.
    Â
    Что касается модульных тестов, то они делают что-то противоположное тому, как я использую их в хорошем коде, например, нарушая разумные изменения и не улавливая реальные ошибки, которые я совершаю. Что является болезненным, но неудивительным - что еще можно было бы ожидать от тестирования единиц , у которых плохой дизайн для начала? Вместо того, чтобы помочь вам улучшить дизайн, модульные тесты часто работают, чтобы сохранить плохой код - например, в моем недавнем проекте обслуживания я регулярно удалял большие куски кода, на который ссылались только устаревшие бессмысленные модульные тесты. BTW Я использую эти знания при написании нового кода: когда я узнаю, что мои модульные тесты, как правило, становятся слишком сложными /хрупкими, это указывает на необходимость исправления некоторых проблем с моей собственной конструкцией.

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

  • Начните с того, что спросите себя - почему они просто не выбрасывали этот Big Ball of Mud ?
    Почему они инвестировали в найм на вас, чтобы поддерживать его? Как правило, причина в том, что это хорошо работает для пользователей, и вы ожидаете, что он будет хорошо работать для пользователей. Они воспринимают это как черный ящик , они не смотрят на (дерьмовый) код, все они видят полезную функциональность.
    Â
    Теперь, если вы когда-либо работали с профессиональным QA, вы быстро поймете, что именно так тестировщики имеют дело с программным обеспечением. Черный ящик, функциональность, качество с точки зрения пользователя - все это темы и темы обеспечения качества.

Хорошо, если вы читали до сих пор, вы можете задаться вопросом, как именно это работает для меня? Это действительно просто.

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

Если мне действительно повезло (не часто), мне больше не нужно волноваться. Что касается худшего сценария, это тоже не так плохо. Если я сделал что-то не так, тестер вернется ко мне через день или два, с ясным объяснением того, что точно пошло не так, и как воспроизвести ошибку, а также их набор регрессионных тестов , расширенный, чтобы легко поймать такие ошибки, если они появятся снова. Неплохо, не так ли?

Следующая замечательная вещь, которая поставляется с QA, - это регулярные циклы регрессии .

  • Некоторые(lame) могут попытаться убедить вас, что вы можете сделать это автоматически - не доверяйте им на этом.
    Вещи, которые отлично работают с хорошим кодом, просто не могут сделать трюк с Big Ball Of Mud .
    Â
    Выполнение теста является громоздким, анализ результатов тестирования требует много усилий, поддержка баз данных и регрессий требует много внимания (больше внимания, чем вы можете себе позволить, если планируете также сосредоточиться на развитии). Вместо того, чтобы тратить время на то, чтобы быть домом всех профессий, пусть профессиональные тестеры сделают это для вас, освободите свой мозг и время для дизайна и кодирования. Просто используйте старый добрый раздел рабочей силы .

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

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


Помимо основных преимуществ, упомянутых выше, на этом пути есть другие, меньшие, но приятные бонусы.

  1. С помощью тестеров вы можете с кем-то обсудить дизайн. Как я писал, документы и код здесь не помогают, когда вы имеете дело с Big Ball of Mud - создаете вакуум, если работаете самостоятельно. Тестер, который запускает ваш код и смотрит на него с разных точек зрения, делает отличного партнера для отскока идей и изучения возможных изменений.

  2. С помощью тестеров вы получаете возможность резервного копирования вашего понимания проблем дизайна. Когда есть только разработчики, жалующиеся на качество кода , это часто звучит как субъективные WTF из-за закрытой двери .
    Â
    Но когда это повторяет парень QA, говорящий что-то вроде component A, было 100 ошибок регрессии для 10 новых функций, в отличие от component B, у которого было 10 ошибок регрессии за 20 новых функций , общение внезапно превращается в целую другую игру.

  3. Последнее, но не менее важное, профессиональное QA помогает продвинуть ваше понимание количества усилий, которые стоит инвестировать в усовершенствования дизайна. Как я уже упоминал, руководство не очень хорошо хранит качество кода WTF .
    Â
    Но когда есть профессиональный QA, со всеми данными, которые они обычно собирают, вы можете придумать материал, который каким-то образом стремится взлететь через мозги менеджеров прямо в эту секретную ячейку с магическим штампом Я одобряю

      

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

ответил gnat 16 FebruaryEurope/MoscowbThu, 16 Feb 2012 16:23:27 +0400000000pmThu, 16 Feb 2012 16:23:27 +040012 2012, 16:23:27
8
  

Один из подходов - написать тесты,

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

Так я лично подхожу к такой ситуации:

  • Начните с изучения, сделайте заметки, нарисуйте диаграммы и спросите себя (и /или ближайшего «местного эксперта») множество вопросов. Вам нужно прочитать код и получить хороший общий обзор проблемы.
  • Определите несколько очевидных проблемных областей и оцените риск беспорядка с ними. Спайк несколько изменений, чтобы увидеть, как произойдут сбои, и получить представление о том, сколько дополнительных усилий потребуется для того, чтобы следовать пути, по которому следовал ваш шип, или сделать что-то еще полностью.
  • Выберите проблему и напишите тесты. Используйте существующие тесты, если они доступны, и если не пишите новые, которые защищают бизнес-логику настолько, насколько возможно.
    • Запишите свой тест, чтобы выполнить сбой, затем исправьте свой тест, чтобы он прошел, а затем замените ваш код на неудачу, как двойную проверку, а затем снова восстановите свой код. Используйте последний шаг «Сбой кода» как возможность пройти через код и определить что-то очевидное, которое вы, возможно, пропустили.
  • Разработайте и спланируйте свое изменение за то, как вы считаете, что код должен быть написан для устранения проблемы. Здесь вам нужно вернуться к требованиям и спецификации (при условии, что у вас есть), и написать свои тесты, как и ваши истории пользователей или функции или любой другой метод, который вы обычно использовали.
  • Напишите новые тесты в соответствии с вашими новыми историями (например, с вашей пересмотренной спецификацией)
  • Реорганизуйте свой код небольшими шагами. Начните с извлечения методов, затем посмотрите на извлечение классов, затем посмотрите на движущиеся методы и классы, а затем на объединение /удаление классов. Всегда выполняйте все простые рефакторинги, потому что это облегчит более сложные рефакторинги. Я бы предложил прочитать Рефакторинг и Рефакторинг для шаблонов , если вы ищете небольшую помощь или вдохновение для руководства своими усилиями.
    • Как вы рефакторинг, вы пытаетесь передать свои оригинальные тесты и нацелиться на то, чтобы ваши новые тесты проходили после завершения рефакторинга. По мере того, как вы это делаете, вы можете обнаружить, что вы выбрали некоторые из исходных тестов, или вам может быть повезло, что вместе прошли старые и новые тесты.

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

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

ответил S.Robins 16 FebruaryEurope/MoscowbThu, 16 Feb 2012 06:29:55 +0400000000amThu, 16 Feb 2012 06:29:55 +040012 2012, 06:29:55
2

Я работаю над сложной базой кода уже более года. Посмотрите, поможет ли вам моя информация:

Ваши идеи верны, к тому времени, когда вы достигнете другой части кода, вы забудете о предыдущей части. Это может быть бесконечный цикл. Важный урок, который нужно убрать, заключается в том, что продукт не может работать, если все части не работают должным образом. Даже если одна часть не работает, продукт не работает. См. Это под другим углом: если вы улучшите одну деталь резко, она все еще НЕ МОЖЕТ НЕ привести к лучшей работе продукта, что является вашей основной целью здесь.

Итак, сначала: не являйтесь разработчиком. Будьте тестером.

Не пытайтесь понять часть по частям. Понимать весь продукт и его работу, когда все части вместе. Из производственной среды (то есть среды без разработки - нет точек отладки), проверьте продукт. Затем, как и каждый тестер, запишите проблемы, с которыми вы сталкиваетесь, в трекер ошибок. Назначьте ему серьезность и приоритет. Поскольку это программное обеспечение существует довольно долгое время, посмотрите, есть ли уже созданный трекер ошибок. Если он уже есть, вам повезло. Добавьте к ним и найдите время и проверьте каждый из существующих. В конце этого цикла вы понимаете продукт с точки зрения пользователя (вы определенно не должны его пропустить), а также точку зрения QA. Разумеется, вы даже можете понять, что строка кода исправит ошибку, а те, кто ее закодировал, не сделали этого, потому что тогда не было никакой реальной потребности.

Второй шаг: наденьте дизайнерский плащ

Разделите продукт на несколько частей (не буквально или в соответствии с вашим удобством, а в соответствии с тем, как они работают вместе). Может быть, ваша работа uptil сейчас или существующие знания могут вступить в игру. Затем попробуйте понять, как они работают друг с другом, а также с 10 зависимыми библиотеками. Затем для каждой отслеживаемой ошибки напишите заметки, идентифицирующие объекты кода (например: это изменение включает в себя модификацию классов X, Y, Z и т. Д.). Вероятно, к концу этого шага вы будете иметь FEW намеки на то, какие проблемы с текущей архитектурой и что можно улучшить.

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

Дом карточек

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

Приоритет ваших проблем

Вам нужно помнить о том, что вы пытаетесь улучшить:

  

Хотите, чтобы продукт был быстрее?

Конечно, да. Но является ли это основной проблемой? Это медленно? Если да, создайте критерии эффективности, определите узкие места и улучшите эти части. Повторите тест.

  

Вы хотите улучшить удобство использования?

Тогда это в значительной степени сторона API /UI.

  

Вы хотите улучшить безопасность?

Тогда это границы, которые вы должны изучать.

Я предоставил только 3 примера, но их гораздо больше можно найти.

Последняя и лучшая документация

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

ответил Sundeep 14 J0000006Europe/Moscow 2012, 17:01:33
0

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

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

Если не хватает спецификаций, начните прослушивать людей для получения дополнительной информации и записать их.

Запишите план изменения кода и разбейте его на разные этапы.

ответил Rudolf Olah 22 MarpmThu, 22 Mar 2012 19:28:01 +04002012-03-22T19:28:01+04:0007 2012, 19:28:01
-1

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

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

ответил Jarrod Roberson 22 MarpmThu, 22 Mar 2012 19:35:58 +04002012-03-22T19:35:58+04:0007 2012, 19:35:58

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132