Статьи

Анатомия вопроса о переполнении стека (после анализа 10 000)

Как сформулировать вопрос в переполнении стека, чтобы получить лучшие ответы

Stack Overflow предлагает отличный интерфейс для доступа ко всем его данным и выполнения любого возможного запроса в базе данных вопросов / ответов. Мы начали использовать эту базу данных, чтобы лучше понять наиболее распространенные вопросы об отладке (что мы пытаемся решить в Takipi ). Узнав об отладке тысяч вопросов, мы также отметили дополнительный бонус: мы стали лучше понимать, что движет сообществом Stack Overflow. img1_01

Эта статья является результатом исследования более 10 000 вопросов переполнения стека. Он суммирует, как вам нужно сформулировать и написать свой вопрос, чтобы получить лучшие и быстрые ответы. Когда мы начали запускать наши сценарии для ответов на запросы, мы не были уверены, что придем к серьезным выводам. Изучив несколько сотен вопросов, мы уже начали распознавать определенные шаблоны, которые мы видели снова и снова в разных языках программирования и темах.

Вы можете найти подробное объяснение того, как мы выполнили тест в конце статьи, но давайте сначала перейдем к лучшей части — результатам. Основными критериями, которые мы задали для «хорошего» вопроса, были получение качественных ответов (от пользователей с высокой репутацией + голосов), относительно быстрый ответ, а также количество голосов и просмотров.

Держать его коротким

Вероятно, самый сильный шаблон, который мы видели во всех различных запросах и сценариях, которые мы запустили, заключался в следующем: чем короче вопрос, тем больше шансов получить быстрый и полезный ответ. Средняя длина «хорошего» вопроса составляла около 1200 символов (~ 3-4 коротких абзаца), тогда как средняя длина стандартного вопроса составляла 1800 символов. Наибольшее влияние длины было на количество просмотров и голосов, где средняя длина основных вопросов была около 50% от стандартных вопросов.

Также нет такой вещи, как слишком короткие — действительно короткие вопросы (около 200-300 символов) получили лучшие результаты.

Длина названия имеет меньшее значение, мы обнаружили. Хотя мы увидели влияние длины вопроса по всем направлениям, кажется, что длина названия незначительно влияет на качество вопроса. Средняя длина заголовка главных вопросов была примерно на 5% короче, чем у стандартных заголовков (47 символов против 50).

01_length

# 1 фактор влияния — репутация аскера

Репутация аскера оказывает огромное влияние на количество и качество ответов и скорость ответа. Кажется, что пользователи с высокой репутацией, как правило, чаще отвечают и пользователям с высокой репутацией. Хотя мы увидели, что на короткие вопросы вероятность получить лучшие и более быстрые ответы на 50-100% выше, на вопрос, заданный пользователем с очень высокой репутацией, вероятность получить лучшие ответы в три раза выше, чем на вопрос, заданный пользователем с низким репутации. Конечно, вы можете сделать вывод, что пользователи с высокой репутацией задают лучшие вопросы. Это определенно верно, но мы видели очень похожие вопросы, задаваемые пользователями с низкой и высокой репутацией, и разница в качестве ответа была очевидна.

Несколько примеров: средняя репутация спрашивающего среди 100 лучших вопросов Java с лучшими ответами (высокая репутация пользователя, который дал ответ + голосов) составила 4500 баллов. Средняя репутация спрашивающего у стандартных вопросов Java была 1500.
Средняя репутация задаваемых вопросов на Ruby, на которые ответили в течение 15 минут, была 2400. Средняя репутация задаваемых вопросов на Ruby, на которые были даны ответы через 24 часа, составила 1300.
Средняя репутация спрашивающего среди 100 самых популярных вопросов Java составляет 3150, по сравнению со средней стандартной репутацией 1100.

02_highRep

Должен ли я использовать фрагменты кода?

Внедрение фрагментов кода в вопрос было одним из немногих протестированных нами параметров, который дал очень четкие результаты для разных языков, но противоположные результаты для разных критериев. Кажется, что вопросы, которые включают фрагменты кода, получают лучшие ответы — ответы с большим количеством голосов и от пользователей с более высокой репутацией. Например, 87% вопросов, связанных с Python, на которые ответили пользователи с очень высокой репутацией, содержали фрагменты кода. 64% средних вопросов по Python включают фрагменты кода. Вопросы, связанные с Ruby, показали аналогичные результаты — 91% главных вопросов включали фрагменты кода по сравнению со средним значением 79%.

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

Используйте меньше тегов

Не большая разница, но, похоже, что по различным критериям, которые мы тестировали, у лучших результатов меньше тегов, чем в среднем. На вопросы, которые получили качественные ответы, более быстрый ответ и большее количество голосов, в среднем приходилось 3 тега (на разных языках они одинаковы). Стандартные вопросы имели около 3,5 — 3,7 меток.

Когда лучше всего разместить вопрос?

Из наших данных не видно, что время суток влияет на результаты. Переполнение стека «час пик» обычно между 11:00 и 17:00 UTC. Около 50% вопросов задаются в эти сроки. Вопросы, задаваемые в эти часы, имели немного больше шансов получить ответ быстрее (примерно на 5-10%), но ответы не были более качественными. Время, когда вопрос был опубликован, не влияет на другие критерии, такие как голоса, количество просмотров или количество ответов.

Как озаглавить свой вопрос — Как попасть в Изобразительное искусство наука о формулировке

Используйте название языка / темы в заголовке

Один из основных скриптов, которые мы использовали, был создан для подсчета и анализа заголовков. Мы сгруппировали слова по программным терминам (например, «строка», «массив», «функция»), языку / теме (например, Ruby, MySQL, C #), отрицательным / положительным словам (например, «не могу», «наихудший», «Лучший», «провал») и многое другое. Самый внятный вывод — если вы хотите получить более быстрые и качественные ответы, используйте тему, о которой вы спрашиваете, в заголовке. Есть вопрос о чем-то в Python? Просто добавьте Python к заголовку.

Некоторые примеры: 36% вопросов, связанных с Ruby, на которые ответили в течение 15 минут, содержали слово «Ruby» в заголовке. Только 15% вопросов, на которые были даны ответы через 24 часа, имели слово «Рубин» в заголовке. 58% вопросов о Java с наибольшим количеством просмотров включают слово «Java». 39% стандартных вопросов по Java содержали слово «Java».

А как насчет использования двух языков в названии? Например, «Почему сравнения строк (CompareTo) быстрее в Java, чем в C #?». Кажется, что использование двух разных языков / тем в названии может уменьшить количество и качество ответов, так как меньше пользователей владеют обоими. Тем не менее, вопросы такого рода выполнялись очень хорошо и с гораздо большей вероятностью давали хорошие результаты.

Неважно, если вы сформулируете заголовок как вопрос или нет

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

Например — «Добавить один метод к нескольким классам» или «Как добавить один и тот же метод к нескольким классам?» получит те же результаты. Единственное отличие, которое мы отметили в исследовании, заключалось в том, что названия, сформулированные как вопросы, с большей вероятностью получат более быстрый ответ (примерно на 10%).

Что-то у вас не работает? Вы не можете решить проблему? Сообщество здесь, чтобы помочь

Заголовок, который указывает, что что-то не работает, или спрашивает об ошибке, обычно получает лучший и быстрый ответ.

Например:

Rspec заглушка не работает

Почему упаковщик не может связаться с http://rubygems.org?

Не могу импортировать мои собственные модули в Python

Похоже, что использование слов, обозначающих неудачу («не может», «невозможно», «не удается», «ошибка», «не работает» и т. Д.), Приводит к лучшим ответам. Например, 22% главных вопросов по Ruby содержали в заголовке отрицательную фразу, в то время как в среднем по стандартным вопросам было 14%.

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

Должен ли я использовать X или Y?

Сравнение различных технологий или методологий — хороший рецепт для качественных ответов. Этот тип вопросов имел существенное значение в каждом списке «топ вопросов», который мы сформировали.

Вот несколько примеров:

Почему я не должен использовать PyPy поверх CPython, если PyPy работает в 6,3 раза быстрее?

Почему char [] предпочтительнее, чем String для паролей?

Почему сравнения строк (CompareTo) быстрее в Java, чем в C #?

Давайте сделаем это интересным

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

Вот несколько примеров главных вопросов:

Почему обработка отсортированного массива быстрее, чем несортированного массива? (6960 голосов)

Почему изменение от 0,1f до 0 снижает производительность в 10 раз? (774 голоса)

Почему parseInt (1/0, 19) возвращает 18? (632 голоса)

Как мы провели тест?

Мы решили сосредоточиться на языковых вопросах, чтобы избежать общих вопросов (таких как «как стать лучшим программистом» или «какой вопрос собеседования для работы») или вопросов, связанных с юмором, которые обычно получают очень высокий балл, но не не представляют типичные проблемы, с которыми сталкиваются разработчики. Мы сосредоточились на 6 языках программирования — Java, Ruby, Python, C ++, Javascript и C #. Первые три языка, которые мы проанализировали, были Java, Ruby и Python — результаты были очень похожи по всем трем, поэтому мы решили пока не анализировать остальные три.

Мы решили сосредоточиться только на вопросах, задаваемых с 2011 года.

Мы выполнили восемь различных запросов и отсортировали вопросы по: голосам, количеству ответов, количеству избранных, времени до первого ответа, времени суток, когда был опубликован вопрос, вопросам, на которые отвечали пользователи с высокой репутацией, вопросам, задаваемым пользователями с высокой репутация, количество просмотров. Затем мы сравнили 300 лучших вопросов в каждом разделе с 300 вопросами, которые получили средний балл по параметру, на котором мы сосредоточились. Это явно не точная наука, и нет «научного рецепта» для получения хороших ответов. Тем не менее, все шаблоны, упомянутые выше, повторяются в разных параметрах и на разных языках, которые мы задавали.

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

Спасибо Дрору Коэну из CodersClan за помощь и Джону Ву, который оправдал свои очки репутации в 120 000+ и помог нам написать запросы.

Примеры запросов

  1. Вопросы пользователей с высокой репутацией и тегом Python
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    ;WITH recordsList
    AS
    (
    SELECT 'http://stackoverflow.com/users/' + CAST(p.OwnerUserId AS VARCHAR(10)) AS OwnerLink,
    'http://stackoverflow.com/questions/' + CAST(p.ID AS VARCHAR(10)) AS QuestionLink,
    p.Score,
    p.ViewCount,
    p.FavoriteCount,
    p.Title,
    LEN(p.Title) AS TitleLength,
    LEN(p.Body) AS BodyLength,
    p.Tags,
    p.CreationDate,
    u.Reputation,
    ROW_NUMBER() OVER
    (PARTITION BY p.OwnerUserId
    ORDER BY p.Score DESC) UserAnswerSequence,
    CASE WHEN CHARINDEX('<code>', p.Body) > 0 THEN 'True' ELSE 'False' END ContainsCodeBlock
    FROM Posts AS p
    INNER JOIN Users As u
    ON p.OwnerUserId = u.Id
    INNER JOIN PostTags AS pt
    ON pt.PostId = p.Id
    WHERE p.PostTypeId = 1 -- <<== Questions
    AND p.CommunityOwnedDate IS NULL -- <<== not WIKI
    AND pt.TagId = 16 -- <<== PYTHON
    )
    SELECT TOP 5000
    OwnerLink,
    QuestionLink,
    Score,
    ViewCount,
    FavoriteCount,
    Title,
    TitleLength,
    BodyLength,
    Tags,
    CreationDate,
    ContainsCodeBlock
    FROM recordsList
    WHERE UserAnswerSequence <= 50
    ORDER BY Reputation DESC, Score DESC
  2. Ответы пользователей с высокой репутацией и тегом Java
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    ;WITH recordsList
    AS
    (
    SELECT 'http://stackoverflow.com/users/' + CAST(p.OwnerUserId AS VARCHAR(10)) AS OwnerLink,
    'http://stackoverflow.com/questions/' + CAST(p.ID AS VARCHAR(10)) AS QuestionLink,
    p.Score,
    p.ViewCount,
    p.FavoriteCount,
    p.Title,
    LEN(p.Title) AS TitleLength,
    LEN(p.Body) AS BodyLength,
    p.Tags,
    p.CreationDate,
    u.Reputation,
    ROW_NUMBER() OVER
    (PARTITION BY pa.OwnerUserId
    ORDER BY pa.Score DESC, p.Score DESC) UserAnswerSequence,
    CASE WHEN pa.CommunityOwnedDate IS NOT NULL THEN 'TRUE' ELSE 'FALSE' END IsAnswerWiki
    FROM Posts AS p
    INNER JOIN Posts As pa
    ON pa.PostTypeId = 2 -- <<== Answer
    AND p.Id = pa.ParentID
    AND p.OwnerUserId <> pa.OwnerUserId -- <<== not come on the same poster
    AND p.AcceptedAnswerId = pa.Id
    INNER JOIN Users As u
    ON pa.OwnerUserId = u.Id
    INNER JOIN PostTags AS pt
    ON pt.PostId = p.Id
    WHERE p.PostTypeId = 1 -- <<== Questions
    AND p.CommunityOwnedDate IS NULL -- <<== not WIKI
    AND pt.TagId = 17 -- <<== JAVA
    AND p.OwnerUserId IS NOT NULL -- <<== user is not deleted
    AND pa.OwnerUserId IS NOT NULL -- <<== user is not deleted
    )
    SELECT TOP 5000
    OwnerLink,
    QuestionLink,
    Score,
    ViewCount,
    FavoriteCount,
    Title,
    TitleLength,
    BodyLength,
    Tags,
    CreationDate,
    IsAnswerWiki
    FROM recordsList
    WHERE UserAnswerSequence <= 50
    ORDER BY Reputation DESC, Score DESC
  3. Сгруппируйте вопросы по часу, когда они были опубликованы (0-23), и укажите время, необходимое для их ответа.
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    ;WITH hourgenerator -- <<== generates 0-23 (24Hour)
    AS
    (
    SELECT 0 AS hourPosted UNION ALL
    SELECT hourPosted + 1
    FROM hourgenerator
    WHERE hourPosted < 23
    ),
    questionsPerHour
    AS
    (
    SELECT CAST(p.CreationDate as DATE) AS [Date],
    DATEPART(Hour, p.CreationDate) AS hourPosted,
    COUNT(*) NumberOfQuestions,
    AVG(DATEDIFF(second, p.CreationDate, pa.CreationDate)) AvgTimeAnswered
    FROM Posts AS p
    INNER JOIN Posts As pa
    ON pa.PostTypeId = 2 -- <<== Answer
    AND p.Id = pa.ParentID
    AND p.OwnerUserId <> pa.OwnerUserId -- <<== not come on the same poster
    AND p.AcceptedAnswerId = pa.Id
    WHERE p.PostTypeId = 1 -- <<== Questions
    AND p.CommunityOwnedDate IS NULL -- <<== not WIKI
    AND DATEDIFF(second, p.CreationDate, pa.CreationDate) >= 0
    AND p.OwnerUserId IS NOT NULL -- <<== user is not deleted
    AND pa.OwnerUserId IS NOT NULL -- <<== user is not deleted
    AND p.CreationDate >= CAST('2013-11-14' AS DATE)
    AND p.CreationDate < DATEADD (dd, 1 ,CAST('2013-11-14' AS DATE))
    GROUP By CAST(p.CreationDate as DATE), DATEPART(Hour, p.CreationDate)
    )
    SELECT COALESCE(qph.[Date], CAST('2013-11-14' AS DATE)) AS [Date],
    hg.hourPosted,
    COALESCE(qph.NumberOfQuestions, 0) AS NumberOfQuestions,
    COALESCE(qph.AvgTimeAnswered, 0) AS AvgTimeAnswered
    FROM hourgenerator AS hg
    LEFT JOIN questionsPerHour AS qph
    ON hg.hourPosted = qph.hourPosted
    ORDER BY hg.hourPosted