Продемонструвати клієнту поганий код?


129

Клієнт попросив мене переробити їх веб-сайт, додаток ASP.NET Webforms, розроблений іншим консультантом. Це здавалося відносно простою роботою, але, подивившись на код, зрозуміло, що це не так.

Ця заявка написана не так добре. Зовсім. Він надзвичайно вразливий до атак на ін'єкцію SQL, бізнес-логіка поширюється на всю програму, є багато дублювання та безвихідний код, який нічого не робить. Крім того, він продовжує викидати винятки, які згладжуються, тому сайт, здається, працює безперебійно.

Моя робота полягає в тому, щоб просто оновити HTML і CSS, але більша частина HTML формується в бізнес-логіці, і це було б кошмаром для розбору. Моя оцінка перероблення довше, ніж клієнт прагнув. Вони запитують, чому так довго.

Як я можу пояснити своєму клієнту, наскільки поганий цей код? На їхню думку, додаток працює чудово, і оновлення має бути швидким одноразовим. Це моє слово проти попереднього консультанта. Як я можу навести прості, конкретні приклади, які зрозуміє нетехнічний клієнт?

Оновлення

Дякую за всі відповіді. Демонстрація атаки ін'єкцій SQL має сенс, і я демонструю це в тестовому середовищі. Це лише одна частина багатьох проблем у цій програмі. Я шукав способів пояснити, чому інші частини (наприклад, що генеруються html у шарі даних) потрібно замінити кращими методами, щоб відбулося оновлення html та css. Тут є багато хороших пропозицій, які я зроблю разом, коли поговорю зі своїм клієнтом.


97
Продемонструвати атаку ін'єкції SQL?
Остін Генлі

30
This application was not written well. At all.Вони майже ніколи не є. :)
haylem

15
Крім демонстрації питань, про які говорить Остін. не варто недооцінювати силу дошки та фломастера. Більшість людей добре реагують на поганий дизайн, пояснений у формі зображення.
Сірекс

3
якщо це не великий - переписати його, якщо він великий - не беріть його
Ren

4
Клієнт говорить про перероблення, і вони думають, що HTML / CSS. Я б використовував терміни "відсутність модульності" і наголошував на "логічному дизайні" проти "презентації". Метафори будівництва будинків корисні. To make a change in the look of the living room, I had to go into the air-conditioning system.У гарному модульному дизайні таких речей не буває.
Фурманатор

Відповіді:


144

Нетехнологи - це не ідіоти (здебільшого). Вони можуть зрозуміти технічний аргумент, якщо тримати його на високому рівні. Виберіть завдання, яке, на вашу думку, повинно бути простим, і пройдіть їх, чому це не так.

Я очікував, що ця зміна буде одним словом в одному файлі. Найімовірніше, що це місце змінилося, здавалося, тут, але коли я змінив його там, він працював лише в одному місці, і він порушив ці 7 інших місць. Коли я зафіксував одне, він зламав ще два місця, викликаючи ефект доміно, тому зміна, на яку я думав, повинна була зайняти 10 хвилин, і закінчилася 2 години. Це лише один приклад. Там набагато більше несподіваних 2-годинних завдань.


10
Зважаючи на вміст такої кількості повідомлень про помилки, "вона розбита __ більше місць" справді здається найкращим способом описати ефект доміно ...
Izkata

4
Правильно, я би зробив більше зв'язку між часом і вартістю. Покажіть їм, скільки ви очікували змін у вартості порівняно з вартістю змін. На мій досвід, клієнти рідко звертають увагу, якщо ви не можете зробити так, що вони витрачають подвійно, потрійно або більше, ніж вони платять інакше.
Тім О'Браєн

87

Структура коду, стиль, технічна заборгованість - це одне, що - принаймні спочатку, поки клієнт не довіряє вам - вам потрібно буде жити.

Уразливості безпеки - інша справа.

Особисто я б зробив оцінку на основі роботи, необхідної з використанням існуючої структури та стилю, одночасно даючи зрозуміти, що з кодовою базою є суттєві проблеми. Я б підніс наслідки для безпеки окремо: продемонструйте злом на базі даних, щоб загнати точку додому під час зустрічі.

Я з великою радістю робив це з попереднім клієнтом із системою подарункових карт лояльності, коли я поставив 5000 фунтів на "мою" карту і змусив його перевірити карту на своєму "до".


38
+1 демонстрація того, наскільки сильною може бути атака SQL-ін'єкції. Зробіть це перед ними. По можливості відеозапис їх реакцій.
Філіп

40
@Philip: ... демонстрація повинна бути переважно в ізольованому середовищі розробки для програми. Знищення їх виробничої бази даних доводить суть, але може втратити контракт (і отримати позов).
FrustratedWithFormsDesigner

19
@FrustratedWithFormsDesigner, якщо у них навіть є середовище для розробників ...
ratchet freak

3
@FrustratedWithFormsDesigner: звичайно протирати базу даних не рекомендується, як би просто і драматично не було. Але може бути так само дивно (для них) витягти приватні дані, а потім (ретельно) змінити деякі суми (наприклад, залишок на подарунковій картці, як це зробив @Michael). Щодо додаткових балів, очевидно, що вам дійсно не потрібно бачити код; почніть із скидання списку таблиць, виберіть кілька цікавих імен, скидання вмісту. Не слід займати занадто багато, щоб пробурити точку, яка настільки вразлива.
Хав'єр

76

Тут є кілька чудових пропозицій щодо того, як передати та повідомити це клієнту. Сподіваємось, вони окупиться за вас.

Тут головний червоний прапор!

Якщо клієнт попросить вас не вносити жодних змін, окрім тих, на які ви погодилися (HTML та CSS), я б передав цей проект і зняв свою заявку.

Навіть при письмовому та добре повідомленому огляді всіх недоліків та проблем безпеки, потенційна відповідальність є занадто великою для мене, щоб мені було комфортно. Навіть якщо клієнт ніколи не вживав жодних юридичних дій або не вимагав виправлень після злому чи порушення; ваше ім’я та репутація все ще прикріплені до роботи!

Ви можете втратити набагато більше, ніж ви готові отримати.


14
+1 для перегляду широкої картини. Якщо ви працюєте над цим і кажете, що ви закінчили, ви можете нести певну відповідальність за помилки та проблеми із безпекою, навіть якщо ви їх лише успадкували. Якщо хтось маніпулював моїми гальмами, а механік відремонтував мій велосипед і просто знищив проблему, я також міг би подумати про їх позов ...
sleske

2
+1 це консультація на уроці займає занадто багато часу, щоб навчитися (і, правда, це складна концепція, яку слід дотримуватися в умовах жорсткої економії). Цінність вашої експертизи є стільки ж функцією роботи, яку ви виконуєте, так і функцією роботи, яку ви відмовляєте.
Тім О'Брайен

2
+1 Це урок, який я навчився важким шляхом, і моя перша справа ледь не пережила його. Часто в цих випадках вартість перерахування всіх «дефектів» та цитування їх виправлення потребує більше зусиль, ніж замовник готовий заплатити.
Catharz

30

Поясніть та, можливо, продемонструйте недолік.
Коли це ваше слово проти його, все, що ви говорите, може бути просто гарячим повітрям, наскільки вони стосуються. Після того, як ви покажете їм, як їх програму можна зловживати через ін'єкцію SQL, раптом ви станете людиною, якій можна довіряти. Вам знадобиться довіра для того, щоб переговоритись. І цього досить для зміни гри, щоб дати вам її.

Будьте добродійними по відношенню до свого попередника.
Це не означає робити вигляд, що на помилки немає, але якщо ви зіткнетеся з поблажливістю, то втрачаєте авторитет. Не кажіть жодного слова про програміста, окрім можливо, щоб принести йому користь від сумнівів. Зосередьтеся на коді, а не на кодері. Змушуючи їх відчувати, що ти "хороший хлопець", дасть вам набагато більше свободи в переговорах. І "хороші хлопці" ніколи не говорять надуманих речей. Пояснюючи клієнту існуючі помилки безпеки (наприклад, вразливості введення SQL), я вважаю за краще сказати щось подібне:

Безпека веб-додатків - це швидко розвивається сфера. Багато інструментів та методів розвитку, яких люди вивчають навіть сьогодні, розвинулися до того, як більшість цих подвигів були добре зрозумілі. Щоб випереджати розвиток безпеки, вам потрібно дуже уважно стежити за цим полем і час від часу навіть змінювати весь стиль розвитку. Більшість програмістів цього не роблять.

Там ми йдемо. Ні слова зла, не сказаного про забудовника; він просто "більшість програмістів", що означає, що він у гарній компанії. А тепер ви продемонстрували, що ви не "більшість програмістів", що дає вам трохи більше авторитету і, можливо, причина, щоб вони платили вам більше грошей.

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

Як мінімум, ви хочете більше часу, щоб закінчити роботу, яку вони вам вже дали. Ви встановили їх достатньо не охороняючи інформацію про вразливість, що, ймовірно, не затримає вас до вашої початкової оцінки. Але переконайтеся, що клієнт знає, що ви є, і не збираєтеся домовлятися як частина цієї угоди.

Зазвичай розробник (ви) вважає за краще переробити всю справу з нуля. І у таких випадках це може бути навіть варіантом. Але навіть тоді клієнт бажає чогось, що може продовжувати його діяльність до тих пір, поки не буде створено нову програму. Це означає , що навіть якщо ви починаєте знову, ви , ймовірно , все - таки доведеться оновити старі додатки небагато.


8
+1 за те, що ніколи не поблажливий. Просто нехай факти говорять самі за себе ...
sleske

4
+1 для "Будьте благодійними щодо свого попередника".
msanford

19

Я почав це як коментар, тому що спочатку я подумав, що це відсторонено, але, мабуть, насправді це не так.

Я б повністю задокументував, що все, що ви вважаєте, має бути перероблене, і чому (що станеться, якщо вони не внесуть змін), та оцінку щодо усунення проблеми. Я би особливо прискіпливо ставився до всього, що ви сприймаєте як загрозу безпеці.

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

Звичайно, ви можете вказати на керування джерелом оригінального коду, який ви успадкували, якщо він коли-небудь трапиться, але буде набагато простіше вказати на цей документ і більш професійно сказати: "Бачте? Я вам це сказав".

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

Однак, як тільки клієнт відкидає ризики, посміхнусь і нести його, якщо вони скажуть виконати роботу в будь-якому випадку або піти.


Будемо сподіватися, що вони фактично використовують управління джерелами.
Бернар

6
Чудова відповідь. Але як той, хто звертався до суду з приводу подібної ситуації, яка включала повну документацію та вихід клієнта, це все одно коштувало мені великих грошей і головного болю.
Стів

5
Хороша ідея в принципі - проте зауважте, що це може бути багато роботи. Це, мабуть, практично лише для великих робочих місць, інакше ви витратите 50 годин на документування проблем за роботу, де ви можете
заплатити

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

2
@WonkotheSane: Це правда, але тільки якщо ви берете проект . Якщо проблеми такі великі, а запланована робота настільки мала, можливо, краще просто відмовитися від проекту. Звичайно, ви все ж повинні документувати свої занепокоєння (безпека та інше), але якщо ви ніколи не працювали над проектом, не повинно бути ризику відповідальності. Зрештою, вам доведеться оцінити, чи бажає ваш клієнт заплатити витрати на прибирання.
sleske

14

Пам’ятайте, що клієнт звертається до вас за допомогою щодо підтримки їх застосування. Ваша робота як професіонала - вказати на будь-які проблеми, які ви знайдете при їх застосуванні. Клієнт, ймовірно, не має уявлення, що ці проблеми існують, і їм слід їх усвідомлювати. Поясніть ці питання таким чином, щоб вони могли зрозуміти, і дозвольте їм вирішити, як вони хочуть діяти.

Використовуйте приклади реального світу, щоб проілюструвати ці проблеми, наприклад, поломка автомобіля або пральна машина, яка потребує ремонту. Зазначити - використовувати приклади, з якими вони вже знайомі. Для пояснення ін'єкції SQL я б просто продемонстрував, що це таке, і чому це проблема.

Зрештою, ви хочете донести, що ви дбаєте про успіх програми, над якою вас просять працювати.


3
Це не що інше, як розбита машина, якщо тільки автомобіль не був побудований з випадкових деталей механіком-любителем. Це як гараж, побудований некомпетентним підрядником, і власник хоче, щоб ОП поставила автоматичну відкривач дверей. ОП виявляє, що гараж небезпечний і потребує негайної капітальної роботи негайно.
кевін клайн

2
Уявіть розбитий автомобіль, який використовує клейку стрічку, щоб утримувати деталі разом, і відключає приладну панель від показу водія або попередження водію, в той час як кермо може впасти в будь-який момент. Це вимагає певної творчості, але для ілюстрації проблеми можна використовувати різні аналогії.
Бернар

Або зверніть увагу на "на замовлення" кабель прискорювача шпагату, який ви можете прив'язати до консолі для ручного прискорення. Технологія "літати дротом" дешево. Практично про все, що вони зробили на Червоному Зеленому Шоу. Те, що вони мають, «працює», але це не дуже, і при помірному огляді здається, що він крихкий і збільшує ризик будь-яких змін.
JustinC

1
Якщо я міг би додати додаткові голоси до цього, я б суто за "Пам'ятайте, що клієнт звертається до вас за допомогою щодо підтримки їх заявки".
Даніель Холлінрак

7

Мені подобається використовувати аналогії, до яких може ставитися клієнт. Обсяг роботи, яку я вклав у виграш на роботу, залежатиме від кількості грошей, яку клієнт мав намір витратити (100 доларів набагато відрізняються від 20 000 доларів). Зауважте, я сказав "намір". Ваша особиста оцінка вартості, що стосується, не означає багато, якщо ви не отримаєте те, що просите.

У вашій ситуації - знову ж залежно від грошей - я можу намалювати вікно з однією лінією, що виходить з кожної сторони, і сказати клієнтові: "Ось як ви зараз візуалізуєте програмне забезпечення. Дані виходять в один кінець і виходять з іншого, все виглядає красиво, чисто і просто ». "Це те, що ви думаєте, як програмне забезпечення виглядає як зсередини", а потім намалюйте третю лінію, що з'єднує дві лінії всередині поля.

Тоді я намалював би ще одне вікно, як і перше, з лініями введення та виводу на зовнішній стороні, за винятком цього разу, я б сказав "Ось як зараз виглядає програмне забезпечення всередині поля". а потім, щоб з'єднати два рядки цього разу, я намалював би випадкову купу спагеті безладу, можливо, з перервами та приєднаннями та писанками.

Нарешті я б сказав: "Тепер те, що ти просиш мене зробити, це це ..." і намалюй просту форму в першій коробці, можливо, невеликий півкільце, що торкається лінії, а потім сказати "але робити це, я" я повинен зробити це ... ", і намалюйте торнадо, який виглядає у формі спіралі навколо лінії, і продовжуйте ...", щоб обійти все це ..... "і вкажіть на спагетті в іншому полі.

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


6

Як я можу пояснити своєму клієнту, наскільки поганий цей код?

Можливо, ви можете використати аналогію, як сантехніка в будинку, яка з часом, після виправлень і переробку, стає настільки непостійною і зв'язаною, що, коли виправляєте одне, впливає і, можливо, зламає щось інше, що потім потребує фіксації, і просто не існує способу дізнатися всі місця, що це відбудеться.

Це моє слово проти попереднього консультанта, тож як я можу насправді дати прості, конкретні приклади, які розуміє нетехнічний клієнт?

Ви маєте рацію, це слово проти того, що візуально створив попередній консультант у їхніх головах. Моя пропозиція - робити те, що ви просите, наводити прості, конкретні приклади. Оскільки це перероблення, покажіть, як фрагмент HTML, визначений у скомпільованому коді, відображається з рештою HTML-сторінки та як зміни, що впливають на це чи не, решту сторінки. Можливо, той самий складений код відображає розмітку після застосування якогось "ділового" правила. Покажіть різницю.

Це важка і ДУЖЕ поширена проблема. Удачі з цим.


6

Будьте чесними і будьте прямими.

Але найголовніше не братися за роботу, яка не відповідає вашим очікуванням. Більшість людей не розуміють, що підрядник може звільнити клієнта, вони можуть і повинні, якщо робота має більше клопоту, ніж варто.


3

Ось я застосував аналогію (хоча я не покладаюся на її ефективність): Уявіть, що їх веб-сайт є фізичною машиною, як механічна друкарня, яка якось приймає вклад.

Вони, напевно, думають про машину як про компонент, який робить X, і інший, який робить Y. Насправді це 20 або більше в основному подібних машин. Деякі з них більше нічого не роблять, усі вони намагаються заздалегідь виконати функції, а інші вже роблять, і ніхто, крім попереднього консультанта, ніколи не бачив нічого подібного раніше.

"Дивіться цю штучку тут, яка аналізує змінні публікації, а потім відправляє цей компонент в кролячу ямку if-elses? Існує не один із них, є один із них на кожній сторінці (або що завгодно), деякі з них санітувати вхід, а деякі не (або не всі), і не читаючи все, я не можу знати, який ".


"Уявіть, що їх веб-сайт - це фізична машина, як механічна друкарня" - і це друк грошей! Але, оскільки він зламаний, він не друкує стільки грошей, скільки міг би ... що повинно їх зачепити, -)
Mawg

2

Один момент, про який ще не було сказано, - це те, що ви можете просто перевищити те, що ваш клієнт насправді хоче від вас у цьому випадку. Досконалість - це чудово і може принести вам багато задоволення від роботи. Але якщо клієнт просто не байдуже, вважає, що поточна продуктивність є "достатньо хорошою" і просто бажає незначних оновлень, можливо, неможливо переконати їх зробити велику інвестицію у вас для капітального ремонту бази даних.

У цей момент вам, мабуть, доведеться вирішити, чи варто стояти на принципах і відмовлятися приймати роботу, яка змусить вас прив’язати своє добре ім'я до бентежної безладної коди чи чи можете ви затримати ніс, вступити, взяти роботу за допомогою клейкої стрічки, і вийдіть зі своєю оплатою.

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

Що стосується ризиків ін'єкцій SQL, то, як інші заявили, ви повинні мати можливість демонструвати небезпеку для них таким чином, що показує ризики, фактично не роблячи нічого руйнівного у виробництві. Але знову ж таки, якщо вони бачать це і не дбають про те, щоб заплатити за його виправлення, ви в цьому випадку зробили свою сумлінну ретельність.


0

Це соус noob, щоб прийти до проекту і запропонувати переписати спочатку, виконати невелику підмножину модифікацій і використовувати ті, щоб проілюструвати, наскільки простішим і дешевшим він міг бути. Тоді у вас є наочний випадок, чому збільшення витрат на розробку більш чистих даних призведе до зниження витрат на обслуговування та більш швидкого розвитку в довгостроковій перспективі, зважаючи на невелику вартість шрифту.

Ніколи не забувайте, що принципово ви просите, щоб вони платили вам, щоб полегшити власне життя, на їхню думку просто необхідність знайти "хлопця", який може використовувати X за ціною Y, і збільшити складність вашого проекту може просто усунути можливість для вас. Це важкий шлях, коли ти місяць переписуєш і маєш зустріч з оригінальним розробником лише для того, щоб усвідомити, що вся програма була написана в надзвичайно контрактному вікні розробником, який повністю розумів усі компроміси, які були зроблені. Якщо додаток всередині виглядає жахливо, але зовні добре працює, як ви кажете, дуже ймовірно, що це буде так. Часто технічна заборгованість в кодовій базі є продуктом обмежень ресурсів, в яких був розроблений код, і якщо вони не створюють команду, а замість цього стискають речі ...

Я просто говорю


0

Я збираюся тут зіграти захисника диявола (дещо в руслі того, що говорить @khrome: "Ви не платите клієнтам, щоб полегшити вам життя"). Я навіть хотів би сказати, що справа, яку ви представили, є занадто однобічною, тому що ви описали справу в цілому. Більшість консультантів, що надходять до нового проекту, засвітили б погане світло попередньому ... Я не кажу, що це ви робите тут, але поки ми не побачимо приклади, я не можу просто прийняти ваше слово.

Це означає, що я спробую вирішити питання, які ви задаєте по пункту:

  • SQL Ін'єкції . Гаразд, тому я думаю, що програміст використовував рядкові конкатенації замість параметризованих запитів та / або збереженої процедури. Це дуже легко виправити, особливо в ADO.NET ... Я особисто зазначив би це клієнтові, але не зробляв із цього великої кількості.
  • HTML генерується у бізнес-логіці, і це було б кошмаром для вирішення . Гаразд, чувак, це одна з тих, де ти мені більше деталізуєш. Якщо ви не використовуєте MVC, така тенденція трапляється ... але це не обов'язково погано ... це одна з тих речей, де більшість програмістів скажуть " goto is bad; never never use it", але ви знаєте, що? Я використовував Goto там, де це мало сенс! Отже, ви впевнені, що вони не використовують допоміжні класи, які мають спільний простір імен як DLL ділового коду? Знову ж таки, це не так складно виділити.
  • бізнес-логіка поширюється на всю програму, є багато дублювання та тупиковий код, який нічого не робить. . І? Клієнт просто просить змінити HTML / CSS. Чому б ти взагалі переймався цими питаннями?
  • він продовжує викидати винятки, які придушуються, тому сайт, здається, працює безперебійно . Знову ж таки, дуже розпливчасто. Винятки є нормальними для будь-яких додатків, тому ми маємо у своєму коді застереження. Якщо вони не загрожують користувальницьким інтерфейсом і не руйнують користувальницький досвід (наприклад, показ HTTP 500 без необхідності), я не думаю, що це те, про що ви повинні піклуватися.

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

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

Редагувати: Я вже бачу, що моя відповідь не є найпопулярнішою (я цього вже очікував), але я стою проти своєї відповіді. Я відредагував це, щоб зробити його менш примхливим. ;-)


-1

Безумовно, атаки ін'єкцій SQL та інші функціональні недоліки у програмі мали перевагу, але ви також можете «продемонструвати» погану якість коду та практики. За допомогою інструментів метрики коду ви можете чітко продемонструвати, наскільки поганий код, і показати йому, на скільки це додасть вартість за майбутні зміни та виправлення помилок. Я не знайомий із середовищем .net, але впевнений, що є декілька, з яких можна підібрати.


Чому суперечка на це? Звичайно, клієнт може бути нетехнологічним, але кодові показники дають цифри, і кожен може зрозуміти це. Особливо, якщо є добре задокументоване, не надто технічне пояснення того, що означають ці цифри
Mawg
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.